1// Copyright 2017 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package prog 5 6import ( 7 "fmt" 8) 9 10// We need to support structs as resources, 11// but for now we just special-case timespec/timeval. 12var timespecRes = &ResourceDesc{ 13 Name: "timespec", 14 Kind: []string{"timespec"}, 15} 16 17func (target *Target) calcResourceCtors(kind []string, precise bool) []*Syscall { 18 19 //fmt.Printf("calcResourceCtors: kind=%+v\n", kind) 20 21 // Find calls that produce the necessary resources. 22 var metas []*Syscall 23 for _, meta := range target.Syscalls { 24 // Recurse into arguments to see if there is an out/inout arg of necessary type. 25 ok := false 26 //if meta.Name != "pipe$9p" { continue } 27 //fmt.Printf("found pipe$9p\n") 28 29 ForeachType(meta, func(typ Type) { 30 if ok { 31 return 32 } 33 switch typ1 := typ.(type) { 34 case *ResourceType: 35 //fmt.Printf(" output: %+v\n", typ1.Desc.Kind) 36 if typ1.Dir() != DirIn && isCompatibleResourceImpl(kind, typ1.Desc.Kind, precise) { 37 ok = true 38 } 39 } 40 }) 41 if ok { 42 metas = append(metas, meta) 43 } 44 } 45 if kind[0] == timespecRes.Name { 46 if c := target.SyscallMap["clock_gettime"]; c != nil { 47 metas = append(metas, c) 48 } 49 } 50 return metas 51} 52 53// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst. 54func (target *Target) isCompatibleResource(dst, src string) bool { 55 if dst == target.any.res16.TypeName || 56 dst == target.any.res32.TypeName || 57 dst == target.any.res64.TypeName || 58 dst == target.any.resdec.TypeName || 59 dst == target.any.reshex.TypeName || 60 dst == target.any.resoct.TypeName { 61 return true 62 } 63 dstRes := target.resourceMap[dst] 64 if dstRes == nil { 65 panic(fmt.Sprintf("unknown resource '%v'", dst)) 66 } 67 srcRes := target.resourceMap[src] 68 if srcRes == nil { 69 panic(fmt.Sprintf("unknown resource '%v'", src)) 70 } 71 return isCompatibleResourceImpl(dstRes.Kind, srcRes.Kind, false) 72} 73 74// isCompatibleResourceImpl returns true if resource of kind src can be passed as an argument of kind dst. 75// If precise is true, then it does not allow passing a less specialized resource (e.g. fd) 76// as a more specialized resource (e.g. socket). Otherwise it does. 77func isCompatibleResourceImpl(dst, src []string, precise bool) bool { 78 //fmt.Printf("isCompatibleResourceImpl: %+v/%v vs %+v/%v\n", dst, len(dst), src, len(src)) 79 if len(dst) > len(src) { 80 // dst is more specialized, e.g dst=socket, src=fd. 81 if precise { 82 //fmt.Printf(" = false1\n") 83 return false 84 } 85 dst = dst[:len(src)] 86 } 87 if len(src) > len(dst) { 88 // src is more specialized, e.g dst=fd, src=socket. 89 src = src[:len(dst)] 90 } 91 for i, k := range dst { 92 if k != src[i] { 93 //fmt.Printf(" = false2\n") 94 return false 95 } 96 } 97 //fmt.Printf(" = true\n") 98 return true 99} 100 101func (target *Target) inputResources(c *Syscall) []*ResourceDesc { 102 var resources []*ResourceDesc 103 ForeachType(c, func(typ Type) { 104 if typ.Dir() == DirOut { 105 return 106 } 107 switch typ1 := typ.(type) { 108 case *ResourceType: 109 if !typ1.IsOptional { 110 resources = append(resources, typ1.Desc) 111 } 112 case *StructType: 113 if target.OS == "linux" && (typ1.Name() == "timespec" || typ1.Name() == "timeval") { 114 resources = append(resources, timespecRes) 115 } 116 } 117 }) 118 return resources 119} 120 121func (target *Target) outputResources(c *Syscall) []*ResourceDesc { 122 var resources []*ResourceDesc 123 ForeachType(c, func(typ Type) { 124 switch typ1 := typ.(type) { 125 case *ResourceType: 126 if typ1.Dir() != DirIn { 127 resources = append(resources, typ1.Desc) 128 } 129 } 130 }) 131 if c.CallName == "clock_gettime" { 132 resources = append(resources, timespecRes) 133 } 134 return resources 135} 136 137func (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[*Syscall]bool, map[*Syscall]string) { 138 supported := make(map[*Syscall]bool) 139 disabled := make(map[*Syscall]string) 140 canCreate := make(map[string]bool) 141 inputResources := make(map[*Syscall][]*ResourceDesc) 142 for c := range enabled { 143 inputResources[c] = target.inputResources(c) 144 145 if c.Name == "pipe$9p" { 146 fmt.Printf("%v: input resource: %+v\n", c.Name, inputResources[c]) 147 } 148 } 149 for { 150 n := len(supported) 151 for c := range enabled { 152 if supported[c] { 153 continue 154 } 155 ready := true 156 for _, res := range inputResources[c] { 157 if !canCreate[res.Name] { 158 ready = false 159 break 160 } 161 } 162 if ready { 163 supported[c] = true 164 for _, res := range target.outputResources(c) { 165 for _, kind := range res.Kind { 166 canCreate[kind] = true 167 } 168 } 169 } 170 } 171 if n == len(supported) { 172 break 173 } 174 } 175 ctors := make(map[string][]string) 176 for c := range enabled { 177 if supported[c] { 178 continue 179 } 180 for _, res := range inputResources[c] { 181 if canCreate[res.Name] { 182 continue 183 } 184 if ctors[res.Name] == nil { 185 var names []string 186 for _, call := range target.calcResourceCtors(res.Kind, true) { 187 names = append(names, call.Name) 188 } 189 ctors[res.Name] = names 190 } 191 disabled[c] = fmt.Sprintf("no syscalls can create resource %v,"+ 192 " enable some syscalls that can create it %v", 193 res.Name, ctors[res.Name]) 194 break 195 } 196 } 197 if len(enabled) != len(supported)+len(disabled) { 198 panic("lost syscalls") 199 } 200 return supported, disabled 201} 202