github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 Field *Field // Syscall field for this arg, nil if there it's not a field. 129 Base *PointerArg // Pointer to the base of the heap object containing this arg. 130 Offset uint64 // Offset of this arg from the base. 131 Stop bool // If set by the callback, subargs of this arg are not visited. 132 parentStack parentStack // Struct and union arguments by which the argument can be reached. 133 } 134 135 func ForeachSubArg(arg Arg, f func(Arg, *ArgCtx)) { 136 foreachArgImpl(arg, nil, &ArgCtx{}, f) 137 } 138 139 func foreachSubArgWithStack(arg Arg, f func(Arg, *ArgCtx)) { 140 foreachArgImpl(arg, nil, &ArgCtx{parentStack: allocStack()}, f) 141 } 142 143 func ForeachArg(c *Call, f func(Arg, *ArgCtx)) { 144 ctx := &ArgCtx{} 145 if c.Ret != nil { 146 foreachArgImpl(c.Ret, nil, ctx, f) 147 } 148 ctx.Parent = &c.Args 149 ctx.Fields = c.Meta.Args 150 for i, arg := range c.Args { 151 foreachArgImpl(arg, &ctx.Fields[i], ctx, f) 152 } 153 } 154 155 func foreachArgImpl(arg Arg, field *Field, ctx *ArgCtx, f func(Arg, *ArgCtx)) { 156 ctx0 := *ctx 157 defer func() { *ctx = ctx0 }() 158 159 if ctx.parentStack != nil { 160 switch arg.Type().(type) { 161 case *StructType, *UnionType: 162 ctx.parentStack = pushStack(ctx.parentStack, arg) 163 } 164 } 165 ctx.Field = field 166 f(arg, ctx) 167 if ctx.Stop { 168 return 169 } 170 switch a := arg.(type) { 171 case *GroupArg: 172 overlayField := 0 173 if typ, ok := a.Type().(*StructType); ok { 174 ctx.Parent = &a.Inner 175 ctx.Fields = typ.Fields 176 overlayField = typ.OverlayField 177 } 178 var totalSize uint64 179 for i, arg1 := range a.Inner { 180 if i == overlayField { 181 ctx.Offset = ctx0.Offset 182 } 183 foreachArgImpl(arg1, nil, ctx, f) 184 size := arg1.Size() 185 ctx.Offset += size 186 if totalSize < ctx.Offset { 187 totalSize = ctx.Offset - ctx0.Offset 188 } 189 } 190 if debug { 191 claimedSize := a.Size() 192 varlen := a.Type().Varlen() 193 if varlen && totalSize > claimedSize || !varlen && totalSize != claimedSize { 194 panic(fmt.Sprintf("bad group arg size %v, should be <= %v for %#v type %#v", 195 totalSize, claimedSize, a, a.Type().Name())) 196 } 197 } 198 case *PointerArg: 199 if a.Res != nil { 200 ctx.Base = a 201 ctx.Offset = 0 202 foreachArgImpl(a.Res, nil, ctx, f) 203 } 204 case *UnionArg: 205 foreachArgImpl(a.Option, nil, ctx, f) 206 } 207 } 208 209 type RequiredFeatures struct { 210 Bitmasks bool 211 Csums bool 212 FaultInjection bool 213 Async bool 214 } 215 216 func (p *Prog) RequiredFeatures() RequiredFeatures { 217 features := RequiredFeatures{} 218 for _, c := range p.Calls { 219 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 220 if a, ok := arg.(*ConstArg); ok { 221 if a.Type().BitfieldOffset() != 0 || a.Type().BitfieldLength() != 0 { 222 features.Bitmasks = true 223 } 224 } 225 if _, ok := arg.Type().(*CsumType); ok { 226 features.Csums = true 227 } 228 }) 229 if c.Props.FailNth > 0 { 230 features.FaultInjection = true 231 } 232 if c.Props.Async { 233 features.Async = true 234 } 235 } 236 return features 237 } 238 239 type CallFlags int 240 241 const ( 242 CallExecuted CallFlags = 1 << iota // was started at all 243 CallFinished // finished executing (rather than blocked forever) 244 CallBlocked // finished but blocked during execution 245 ) 246 247 type CallInfo struct { 248 Flags CallFlags 249 Errno int 250 Signal []uint64 251 } 252 253 const ( 254 fallbackSignalErrno = iota 255 fallbackSignalErrnoBlocked 256 fallbackSignalCtor 257 fallbackSignalFlags 258 // This allows us to have 2M syscalls and leaves 8 bits for 256 errno values. 259 // Linux currently have 133 errno's. Larger errno values will be truncated, 260 // which is acceptable for fallback coverage. 261 fallbackCallMask = 0x1fffff 262 ) 263 264 func (p *Prog) FallbackSignal(info []CallInfo) { 265 resources := make(map[*ResultArg]*Call) 266 for i, c := range p.Calls { 267 inf := &info[i] 268 if inf.Flags&CallExecuted == 0 { 269 continue 270 } 271 id := c.Meta.ID 272 typ := fallbackSignalErrno 273 if inf.Flags&CallFinished != 0 && inf.Flags&CallBlocked != 0 { 274 typ = fallbackSignalErrnoBlocked 275 } 276 inf.Signal = append(inf.Signal, encodeFallbackSignal(typ, id, inf.Errno)) 277 if c.Meta.Attrs.BreaksReturns { 278 break 279 } 280 if inf.Errno != 0 { 281 continue 282 } 283 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 284 if a, ok := arg.(*ResultArg); ok { 285 resources[a] = c 286 } 287 }) 288 // Specifically look only at top-level arguments, 289 // deeper arguments can produce too much false signal. 290 flags := 0 291 for _, arg := range c.Args { 292 flags = extractArgSignal(arg, id, flags, inf, resources) 293 } 294 if flags != 0 { 295 inf.Signal = append(inf.Signal, 296 encodeFallbackSignal(fallbackSignalFlags, id, flags)) 297 } 298 } 299 } 300 301 func extractArgSignal(arg Arg, callID, flags int, inf *CallInfo, resources map[*ResultArg]*Call) int { 302 switch a := arg.(type) { 303 case *ResultArg: 304 flags <<= 1 305 if a.Res != nil { 306 ctor := resources[a.Res] 307 if ctor != nil { 308 inf.Signal = append(inf.Signal, 309 encodeFallbackSignal(fallbackSignalCtor, callID, ctor.Meta.ID)) 310 } 311 } else { 312 if a.Val != a.Type().(*ResourceType).SpecialValues()[0] { 313 flags |= 1 314 } 315 } 316 case *ConstArg: 317 const width = 3 318 flags <<= width 319 switch typ := a.Type().(type) { 320 case *FlagsType: 321 if typ.BitMask { 322 for i, v := range typ.Vals { 323 if a.Val&v != 0 { 324 flags ^= 1 << (uint(i) % width) 325 } 326 } 327 } else { 328 for i, v := range typ.Vals { 329 if a.Val == v { 330 flags |= i % (1 << width) 331 break 332 } 333 } 334 } 335 case *LenType: 336 flags <<= 1 337 if a.Val == 0 { 338 flags |= 1 339 } 340 } 341 case *PointerArg: 342 flags <<= 1 343 if a.IsSpecial() { 344 flags |= 1 345 } 346 } 347 return flags 348 } 349 350 func DecodeFallbackSignal(s uint64) (callID, errno int) { 351 typ, id, aux := decodeFallbackSignal(s) 352 switch typ { 353 case fallbackSignalErrno, fallbackSignalErrnoBlocked: 354 return id, aux 355 case fallbackSignalCtor, fallbackSignalFlags: 356 return id, 0 357 default: 358 panic(fmt.Sprintf("bad fallback signal type %v", typ)) 359 } 360 } 361 362 func encodeFallbackSignal(typ, id, aux int) uint64 { 363 checkMaxCallID(id) 364 if typ & ^7 != 0 { 365 panic(fmt.Sprintf("bad fallback signal type %v", typ)) 366 } 367 return uint64(typ) | uint64(id&fallbackCallMask)<<3 | uint64(aux)<<24 368 } 369 370 func decodeFallbackSignal(s uint64) (typ, id, aux int) { 371 return int(s & 7), int((s >> 3) & fallbackCallMask), int(s >> 24) 372 } 373 374 func checkMaxCallID(id int) { 375 if id & ^fallbackCallMask != 0 { 376 panic(fmt.Sprintf("too many syscalls, have %v, max supported %v", id, fallbackCallMask+1)) 377 } 378 } 379 380 type AssetType int 381 382 const ( 383 MountInRepro AssetType = iota 384 ) 385 386 func (p *Prog) ForEachAsset(cb func(name string, typ AssetType, r io.Reader, c *Call)) { 387 for id, c := range p.Calls { 388 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 389 a, ok := arg.(*DataArg) 390 if !ok || a.Type().(*BufferType).Kind != BufferCompressed { 391 return 392 } 393 data, dtor := image.MustDecompress(a.Data()) 394 defer dtor() 395 if len(data) == 0 { 396 return 397 } 398 cb(fmt.Sprintf("mount_%v", id), MountInRepro, bytes.NewReader(data), c) 399 }) 400 } 401 } 402 403 func (p *Prog) ContainsAny() bool { 404 for _, c := range p.Calls { 405 if p.Target.CallContainsAny(c) { 406 return true 407 } 408 } 409 return false 410 }