github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/resources.go (about) 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 4 package prog 5 6 import ( 7 "fmt" 8 ) 9 10 var ( 11 // We need to support structs as resources, 12 // but for now we just special-case timespec/timeval. 13 timespecRes = &ResourceDesc{ 14 Name: "timespec", 15 Kind: []string{"timespec"}, 16 } 17 // On one hand these are resources, but they don't have constructors. 18 // It can make sense to provide generic support for such things, 19 // but for now we just special-case them. 20 filenameRes = &ResourceDesc{ 21 Name: "filename", 22 Kind: []string{"filename"}, 23 } 24 vmaRes = &ResourceDesc{ 25 Name: "vma", 26 Kind: []string{"vma"}, 27 } 28 ) 29 30 func (target *Target) calcResourceCtors(res *ResourceDesc, preciseOnly bool) []ResourceCtor { 31 var ret []ResourceCtor 32 for _, ctor := range res.Ctors { 33 if !preciseOnly || ctor.Precise { 34 ret = append(ret, ctor) 35 } 36 } 37 if res.Kind[0] == timespecRes.Name { 38 if c := target.SyscallMap["clock_gettime"]; c != nil { 39 ret = append(ret, ResourceCtor{c, true}) 40 } 41 } 42 return ret 43 } 44 45 func (target *Target) populateResourceCtors() { 46 // Find resources that are created by each call. 47 callsResources := make([][]*ResourceDesc, len(target.Syscalls)) 48 for _, meta := range target.Syscalls { 49 dedup := make(map[*ResourceDesc]bool) 50 ForeachCallType(meta, func(typ Type, ctx *TypeCtx) { 51 if typ.Optional() { 52 ctx.Stop = true 53 return 54 } 55 switch typ1 := typ.(type) { 56 case *UnionType: 57 ctx.Stop = true 58 case *ResourceType: 59 if ctx.Dir == DirIn || dedup[typ1.Desc] || meta.Attrs.Disabled { 60 break 61 } 62 dedup[typ1.Desc] = true 63 meta.usesResources = append(meta.usesResources, typ1.Desc) 64 if !meta.Attrs.NoGenerate { 65 callsResources[meta.ID] = append(callsResources[meta.ID], typ1.Desc) 66 meta.createsResources = append(meta.createsResources, typ1.Desc) 67 } 68 } 69 }) 70 } 71 72 if c := target.SyscallMap["clock_gettime"]; c != nil { 73 c.usesResources = append(c.usesResources, timespecRes) 74 c.createsResources = append(c.createsResources, timespecRes) 75 callsResources[c.ID] = append(callsResources[c.ID], timespecRes) 76 } 77 78 for _, c := range target.Syscalls { 79 c.inputResources = target.getInputResources(c) 80 c.usesResources = append(c.usesResources, c.inputResources...) 81 } 82 83 // Populate resource ctors accounting for resource compatibility. 84 for _, res := range target.Resources { 85 for call, callResources := range callsResources { 86 preciseOk := false 87 impreciseOk := false 88 for _, callRes := range callResources { 89 if preciseOk && impreciseOk { 90 break 91 } 92 if isCompatibleResourceImpl(res.Kind, callRes.Kind, true) { 93 preciseOk = true 94 } 95 if isCompatibleResourceImpl(res.Kind, callRes.Kind, false) { 96 impreciseOk = true 97 } 98 } 99 if preciseOk { 100 res.Ctors = append(res.Ctors, ResourceCtor{target.Syscalls[call], true}) 101 } 102 if impreciseOk { 103 res.Ctors = append(res.Ctors, ResourceCtor{target.Syscalls[call], false}) 104 } 105 } 106 } 107 } 108 109 // isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst. 110 func (target *Target) isCompatibleResource(dst, src string) bool { 111 if target.isAnyRes(dst) { 112 return true 113 } 114 if target.isAnyRes(src) { 115 return false 116 } 117 dstRes := target.resourceMap[dst] 118 if dstRes == nil { 119 panic(fmt.Sprintf("unknown resource %q", dst)) 120 } 121 srcRes := target.resourceMap[src] 122 if srcRes == nil { 123 panic(fmt.Sprintf("unknown resource %q", src)) 124 } 125 return isCompatibleResourceImpl(dstRes.Kind, srcRes.Kind, false) 126 } 127 128 // isCompatibleResourceImpl returns true if resource of kind src can be passed as an argument of kind dst. 129 // If precise is true, then it does not allow passing a less specialized resource (e.g. fd) 130 // as a more specialized resource (e.g. socket). Otherwise it does. 131 func isCompatibleResourceImpl(dst, src []string, precise bool) bool { 132 if len(dst) > len(src) { 133 // Destination resource is more specialized, e.g dst=socket, src=fd. 134 if precise { 135 return false 136 } 137 dst = dst[:len(src)] 138 } 139 if len(src) > len(dst) { 140 // Source resource is more specialized, e.g dst=fd, src=socket. 141 src = src[:len(dst)] 142 } 143 for i, k := range dst { 144 if k != src[i] { 145 return false 146 } 147 } 148 return true 149 } 150 151 func (target *Target) getInputResources(c *Syscall) []*ResourceDesc { 152 dedup := make(map[*ResourceDesc]bool) 153 var resources []*ResourceDesc 154 ForeachCallType(c, func(typ Type, ctx *TypeCtx) { 155 if ctx.Dir == DirOut { 156 return 157 } 158 switch typ1 := typ.(type) { 159 case *ResourceType: 160 if !ctx.Optional && !dedup[typ1.Desc] { 161 dedup[typ1.Desc] = true 162 resources = append(resources, typ1.Desc) 163 } 164 case *StructType: 165 if target.OS == "linux" && !dedup[timespecRes] && (typ1.Name() == "timespec" || typ1.Name() == "timeval") { 166 dedup[timespecRes] = true 167 resources = append(resources, timespecRes) 168 } 169 } 170 }) 171 return resources 172 } 173 174 func (target *Target) transitivelyEnabled(enabled map[*Syscall]bool) (map[*Syscall]bool, map[string]bool) { 175 supported := make(map[*Syscall]bool, len(enabled)) 176 canCreate := make(map[string]bool, len(enabled)) 177 for { 178 n := len(supported) 179 nextCall: 180 for c := range enabled { 181 if supported[c] { 182 continue 183 } 184 for _, res := range c.inputResources { 185 if !canCreate[res.Name] { 186 continue nextCall 187 } 188 } 189 supported[c] = true 190 for _, res := range c.createsResources { 191 for _, kind := range res.Kind { 192 canCreate[kind] = true 193 } 194 } 195 } 196 if n == len(supported) { 197 break 198 } 199 } 200 return supported, canCreate 201 } 202 203 func (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[*Syscall]bool, map[*Syscall]string) { 204 supported, canCreate := target.transitivelyEnabled(enabled) 205 disabled := make(map[*Syscall]string) 206 ctors := make(map[string][]string) 207 for c := range enabled { 208 if supported[c] { 209 continue 210 } 211 for _, res := range c.inputResources { 212 if canCreate[res.Name] { 213 continue 214 } 215 if ctors[res.Name] == nil { 216 var names []string 217 for _, ctor := range target.calcResourceCtors(res, true) { 218 names = append(names, ctor.Call.Name) 219 } 220 if len(names) > 5 { 221 names = append(names[:3], "...") 222 } 223 ctors[res.Name] = names 224 } 225 disabled[c] = fmt.Sprintf("%v %v", res.Name, ctors[res.Name]) 226 break 227 } 228 } 229 if len(enabled) != len(supported)+len(disabled) { 230 panic("lost syscalls") 231 } 232 return supported, disabled 233 }