github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/analysis.go (about) 1 // Copyright 2015 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 // Conservative resource-related analysis of programs. 5 // The analysis figures out what files descriptors are [potentially] opened 6 // at a particular point in program, what pages are [potentially] mapped, 7 // what files were already referenced in calls, etc. 8 9 package prog 10 11 import ( 12 "bytes" 13 "fmt" 14 "io" 15 16 "github.com/google/syzkaller/pkg/image" 17 ) 18 19 type state struct { 20 target *Target 21 ct *ChoiceTable 22 corpus []*Prog 23 files map[string]bool 24 resources map[string][]*ResultArg 25 strings map[string]bool 26 ma *memAlloc 27 va *vmaAlloc 28 } 29 30 // analyze analyzes the program p up to but not including call c. 31 func analyze(ct *ChoiceTable, corpus []*Prog, p *Prog, c *Call) *state { 32 s := newState(p.Target, ct, corpus) 33 resources := true 34 for _, c1 := range p.Calls { 35 if c1 == c { 36 resources = false 37 } 38 s.analyzeImpl(c1, resources) 39 } 40 return s 41 } 42 43 func newState(target *Target, ct *ChoiceTable, corpus []*Prog) *state { 44 s := &state{ 45 target: target, 46 ct: ct, 47 corpus: corpus, 48 files: make(map[string]bool), 49 resources: make(map[string][]*ResultArg), 50 strings: make(map[string]bool), 51 ma: newMemAlloc(target.NumPages * target.PageSize), 52 va: newVmaAlloc(target.NumPages), 53 } 54 return s 55 } 56 57 func (s *state) analyze(c *Call) { 58 s.analyzeImpl(c, true) 59 } 60 61 func (s *state) analyzeImpl(c *Call, resources bool) { 62 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 63 switch a := arg.(type) { 64 case *PointerArg: 65 switch { 66 case a.IsSpecial(): 67 case a.VmaSize != 0: 68 s.va.noteAlloc(a.Address/s.target.PageSize, a.VmaSize/s.target.PageSize) 69 case a.Res != nil: 70 s.ma.noteAlloc(a.Address, a.Res.Size()) 71 } 72 } 73 switch typ := arg.Type().(type) { 74 case *ResourceType: 75 a := arg.(*ResultArg) 76 if resources && a.Dir() != DirIn { 77 s.resources[typ.Desc.Name] = append(s.resources[typ.Desc.Name], a) 78 // TODO: negative PIDs and add them as well (that's process groups). 79 } 80 case *BufferType: 81 a := arg.(*DataArg) 82 if a.Dir() != DirOut && len(a.Data()) != 0 && 83 (typ.Kind == BufferString || typ.Kind == BufferFilename) { 84 val := string(a.Data()) 85 // Remove trailing zero padding. 86 for len(val) >= 2 && val[len(val)-1] == 0 && val[len(val)-2] == 0 { 87 val = val[:len(val)-1] 88 } 89 switch typ.Kind { 90 case BufferString: 91 s.strings[val] = true 92 case BufferFilename: 93 if len(val) < 3 || escapingFilename(val) { 94 // This is not our file, probalby one of specialFiles. 95 return 96 } 97 if val[len(val)-1] == 0 { 98 val = val[:len(val)-1] 99 } 100 s.files[val] = true 101 } 102 } 103 } 104 }) 105 } 106 107 type parentStack []Arg 108 109 func allocStack() parentStack { 110 // Let's save some allocations during stack traversal. 111 return make([]Arg, 0, 4) 112 } 113 114 func pushStack(ps parentStack, a Arg) parentStack { 115 return append(ps, a) 116 } 117 118 func popStack(ps parentStack) (parentStack, Arg) { 119 if len(ps) > 0 { 120 return ps[:len(ps)-1], ps[len(ps)-1] 121 } 122 return ps, nil 123 } 124 125 type ArgCtx struct { 126 Parent *[]Arg // GroupArg.Inner (for structs) or Call.Args containing this arg. 127 Fields []Field // Fields of the parent struct/syscall. 128 Base *PointerArg // Pointer to the base of the heap object containing this arg. 129 Offset uint64 // Offset of this arg from the base. 130 Stop bool // If set by the callback, subargs of this arg are not visited. 131 parentStack parentStack // Struct and union arguments by which the argument can be reached. 132 } 133 134 func ForeachSubArg(arg Arg, f func(Arg, *ArgCtx)) { 135 foreachArgImpl(arg, &ArgCtx{}, f) 136 } 137 138 func foreachSubArgWithStack(arg Arg, f func(Arg, *ArgCtx)) { 139 foreachArgImpl(arg, &ArgCtx{parentStack: allocStack()}, f) 140 } 141 142 func ForeachArg(c *Call, f func(Arg, *ArgCtx)) { 143 ctx := &ArgCtx{} 144 if c.Ret != nil { 145 foreachArgImpl(c.Ret, ctx, f) 146 } 147 ctx.Parent = &c.Args 148 ctx.Fields = c.Meta.Args 149 for _, arg := range c.Args { 150 foreachArgImpl(arg, ctx, f) 151 } 152 } 153 154 func foreachArgImpl(arg Arg, ctx *ArgCtx, f func(Arg, *ArgCtx)) { 155 ctx0 := *ctx 156 defer func() { *ctx = ctx0 }() 157 158 if ctx.parentStack != nil { 159 switch arg.Type().(type) { 160 case *StructType, *UnionType: 161 ctx.parentStack = pushStack(ctx.parentStack, arg) 162 } 163 } 164 f(arg, ctx) 165 if ctx.Stop { 166 return 167 } 168 switch a := arg.(type) { 169 case *GroupArg: 170 overlayField := 0 171 if typ, ok := a.Type().(*StructType); ok { 172 ctx.Parent = &a.Inner 173 ctx.Fields = typ.Fields 174 overlayField = typ.OverlayField 175 } 176 var totalSize uint64 177 for i, arg1 := range a.Inner { 178 if i == overlayField { 179 ctx.Offset = ctx0.Offset 180 } 181 foreachArgImpl(arg1, ctx, f) 182 size := arg1.Size() 183 ctx.Offset += size 184 if totalSize < ctx.Offset { 185 totalSize = ctx.Offset - ctx0.Offset 186 } 187 } 188 if debug { 189 claimedSize := a.Size() 190 varlen := a.Type().Varlen() 191 if varlen && totalSize > claimedSize || !varlen && totalSize != claimedSize { 192 panic(fmt.Sprintf("bad group arg size %v, should be <= %v for %#v type %#v", 193 totalSize, claimedSize, a, a.Type().Name())) 194 } 195 } 196 case *PointerArg: 197 if a.Res != nil { 198 ctx.Base = a 199 ctx.Offset = 0 200 foreachArgImpl(a.Res, ctx, f) 201 } 202 case *UnionArg: 203 foreachArgImpl(a.Option, ctx, f) 204 } 205 } 206 207 type RequiredFeatures struct { 208 Bitmasks bool 209 Csums bool 210 FaultInjection bool 211 Async bool 212 } 213 214 func (p *Prog) RequiredFeatures() RequiredFeatures { 215 features := RequiredFeatures{} 216 for _, c := range p.Calls { 217 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 218 if a, ok := arg.(*ConstArg); ok { 219 if a.Type().BitfieldOffset() != 0 || a.Type().BitfieldLength() != 0 { 220 features.Bitmasks = true 221 } 222 } 223 if _, ok := arg.Type().(*CsumType); ok { 224 features.Csums = true 225 } 226 }) 227 if c.Props.FailNth > 0 { 228 features.FaultInjection = true 229 } 230 if c.Props.Async { 231 features.Async = true 232 } 233 } 234 return features 235 } 236 237 type CallFlags int 238 239 const ( 240 CallExecuted CallFlags = 1 << iota // was started at all 241 CallFinished // finished executing (rather than blocked forever) 242 CallBlocked // finished but blocked during execution 243 ) 244 245 type CallInfo struct { 246 Flags CallFlags 247 Errno int 248 Signal []uint32 249 } 250 251 const ( 252 fallbackSignalErrno = iota 253 fallbackSignalErrnoBlocked 254 fallbackSignalCtor 255 fallbackSignalFlags 256 // This allows us to have 2M syscalls and leaves 8 bits for 256 errno values. 257 // Linux currently have 133 errno's. Larger errno values will be truncated, 258 // which is acceptable for fallback coverage. 259 fallbackCallMask = 0x1fffff 260 ) 261 262 func (p *Prog) FallbackSignal(info []CallInfo) { 263 resources := make(map[*ResultArg]*Call) 264 for i, c := range p.Calls { 265 inf := &info[i] 266 if inf.Flags&CallExecuted == 0 { 267 continue 268 } 269 id := c.Meta.ID 270 typ := fallbackSignalErrno 271 if inf.Flags&CallFinished != 0 && inf.Flags&CallBlocked != 0 { 272 typ = fallbackSignalErrnoBlocked 273 } 274 inf.Signal = append(inf.Signal, encodeFallbackSignal(typ, id, inf.Errno)) 275 if c.Meta.Attrs.BreaksReturns { 276 break 277 } 278 if inf.Errno != 0 { 279 continue 280 } 281 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 282 if a, ok := arg.(*ResultArg); ok { 283 resources[a] = c 284 } 285 }) 286 // Specifically look only at top-level arguments, 287 // deeper arguments can produce too much false signal. 288 flags := 0 289 for _, arg := range c.Args { 290 flags = extractArgSignal(arg, id, flags, inf, resources) 291 } 292 if flags != 0 { 293 inf.Signal = append(inf.Signal, 294 encodeFallbackSignal(fallbackSignalFlags, id, flags)) 295 } 296 } 297 } 298 299 func extractArgSignal(arg Arg, callID, flags int, inf *CallInfo, resources map[*ResultArg]*Call) int { 300 switch a := arg.(type) { 301 case *ResultArg: 302 flags <<= 1 303 if a.Res != nil { 304 ctor := resources[a.Res] 305 if ctor != nil { 306 inf.Signal = append(inf.Signal, 307 encodeFallbackSignal(fallbackSignalCtor, callID, ctor.Meta.ID)) 308 } 309 } else { 310 if a.Val != a.Type().(*ResourceType).SpecialValues()[0] { 311 flags |= 1 312 } 313 } 314 case *ConstArg: 315 const width = 3 316 flags <<= width 317 switch typ := a.Type().(type) { 318 case *FlagsType: 319 if typ.BitMask { 320 for i, v := range typ.Vals { 321 if a.Val&v != 0 { 322 flags ^= 1 << (uint(i) % width) 323 } 324 } 325 } else { 326 for i, v := range typ.Vals { 327 if a.Val == v { 328 flags |= i % (1 << width) 329 break 330 } 331 } 332 } 333 case *LenType: 334 flags <<= 1 335 if a.Val == 0 { 336 flags |= 1 337 } 338 } 339 case *PointerArg: 340 flags <<= 1 341 if a.IsSpecial() { 342 flags |= 1 343 } 344 } 345 return flags 346 } 347 348 func DecodeFallbackSignal(s uint32) (callID, errno int) { 349 typ, id, aux := decodeFallbackSignal(s) 350 switch typ { 351 case fallbackSignalErrno, fallbackSignalErrnoBlocked: 352 return id, aux 353 case fallbackSignalCtor, fallbackSignalFlags: 354 return id, 0 355 default: 356 panic(fmt.Sprintf("bad fallback signal type %v", typ)) 357 } 358 } 359 360 func encodeFallbackSignal(typ, id, aux int) uint32 { 361 checkMaxCallID(id) 362 if typ & ^7 != 0 { 363 panic(fmt.Sprintf("bad fallback signal type %v", typ)) 364 } 365 return uint32(typ) | uint32(id&fallbackCallMask)<<3 | uint32(aux)<<24 366 } 367 368 func decodeFallbackSignal(s uint32) (typ, id, aux int) { 369 return int(s & 7), int((s >> 3) & fallbackCallMask), int(s >> 24) 370 } 371 372 func checkMaxCallID(id int) { 373 if id & ^fallbackCallMask != 0 { 374 panic(fmt.Sprintf("too many syscalls, have %v, max supported %v", id, fallbackCallMask+1)) 375 } 376 } 377 378 type AssetType int 379 380 const ( 381 MountInRepro AssetType = iota 382 ) 383 384 func (p *Prog) ForEachAsset(cb func(name string, typ AssetType, r io.Reader)) { 385 for id, c := range p.Calls { 386 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 387 a, ok := arg.(*DataArg) 388 if !ok || a.Type().(*BufferType).Kind != BufferCompressed { 389 return 390 } 391 data, dtor := image.MustDecompress(a.Data()) 392 defer dtor() 393 if len(data) == 0 { 394 return 395 } 396 cb(fmt.Sprintf("mount_%v", id), MountInRepro, bytes.NewReader(data)) 397 }) 398 } 399 } 400 401 func (p *Prog) ContainsAny() bool { 402 for _, c := range p.Calls { 403 if p.Target.CallContainsAny(c) { 404 return true 405 } 406 } 407 return false 408 }