github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/prog/rand.go (about) 1 // Copyright 2015/2016 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 "bytes" 8 "fmt" 9 "math" 10 "math/rand" 11 "path/filepath" 12 "sort" 13 "strings" 14 15 "github.com/google/syzkaller/pkg/ifuzz" 16 ) 17 18 const ( 19 // "Recommended" number of calls in programs that we try to aim at during fuzzing. 20 RecommendedCalls = 30 21 // "Recommended" max number of calls in programs. 22 // If we receive longer programs from hub/corpus we discard them. 23 MaxCalls = 40 24 ) 25 26 type randGen struct { 27 *rand.Rand 28 target *Target 29 inGenerateResource bool 30 inPatchConditional bool 31 recDepth map[string]int 32 } 33 34 func newRand(target *Target, rs rand.Source) *randGen { 35 return &randGen{ 36 Rand: rand.New(rs), 37 target: target, 38 recDepth: make(map[string]int), 39 } 40 } 41 42 func (r *randGen) rand(n int) uint64 { 43 return uint64(r.Intn(n)) 44 } 45 46 func (r *randGen) randRange(begin, end uint64) uint64 { 47 return begin + uint64(r.Intn(int(end-begin+1))) 48 } 49 50 func (r *randGen) bin() bool { 51 return r.Intn(2) == 0 52 } 53 54 func (r *randGen) oneOf(n int) bool { 55 return r.Intn(n) == 0 56 } 57 58 func (r *randGen) rand64() uint64 { 59 v := uint64(r.Int63()) 60 if r.bin() { 61 v |= 1 << 63 62 } 63 return v 64 } 65 66 var ( 67 // Some potentially interesting integers. 68 specialInts = []uint64{ 69 0, 1, 31, 32, 63, 64, 127, 128, 70 129, 255, 256, 257, 511, 512, 71 1023, 1024, 1025, 2047, 2048, 4095, 4096, 72 (1 << 15) - 1, (1 << 15), (1 << 15) + 1, 73 (1 << 16) - 1, (1 << 16), (1 << 16) + 1, 74 (1 << 31) - 1, (1 << 31), (1 << 31) + 1, 75 (1 << 32) - 1, (1 << 32), (1 << 32) + 1, 76 (1 << 63) - 1, (1 << 63), (1 << 63) + 1, 77 (1 << 64) - 1, 78 } 79 // The indexes (exclusive) for the maximum specialInts values that fit in 1, 2, ... 8 bytes. 80 specialIntIndex [9]int 81 ) 82 83 func init() { 84 sort.Slice(specialInts, func(i, j int) bool { 85 return specialInts[i] < specialInts[j] 86 }) 87 for i := range specialIntIndex { 88 bitSize := uint64(8 * i) 89 specialIntIndex[i] = sort.Search(len(specialInts), func(i int) bool { 90 return specialInts[i]>>bitSize != 0 91 }) 92 } 93 } 94 95 func (r *randGen) randInt64() uint64 { 96 return r.randInt(64) 97 } 98 99 func (r *randGen) randInt(bits uint64) uint64 { 100 v := r.rand64() 101 switch { 102 case r.nOutOf(100, 182): 103 v %= 10 104 case bits >= 8 && r.nOutOf(50, 82): 105 v = specialInts[r.Intn(specialIntIndex[bits/8])] 106 case r.nOutOf(10, 32): 107 v %= 256 108 case r.nOutOf(10, 22): 109 v %= 4 << 10 110 case r.nOutOf(10, 12): 111 v %= 64 << 10 112 default: 113 v %= 1 << 31 114 } 115 switch { 116 case r.nOutOf(100, 107): 117 case r.nOutOf(5, 7): 118 v = uint64(-int64(v)) 119 default: 120 v <<= uint(r.Intn(int(bits))) 121 } 122 return truncateToBitSize(v, bits) 123 } 124 125 func truncateToBitSize(v, bitSize uint64) uint64 { 126 if bitSize == 0 || bitSize > 64 { 127 panic(fmt.Sprintf("invalid bitSize value: %d", bitSize)) 128 } 129 return v & uint64(1<<bitSize-1) 130 } 131 132 func (r *randGen) randRangeInt(begin, end, bitSize, align uint64) uint64 { 133 if r.oneOf(100) { 134 return r.randInt(bitSize) 135 } 136 if align != 0 { 137 if begin == 0 && int64(end) == -1 { 138 // Special [0:-1] range for all possible values. 139 end = uint64(1<<bitSize - 1) 140 } 141 endAlign := (end - begin) / align 142 return begin + r.randRangeInt(0, endAlign, bitSize, 0)*align 143 } 144 return begin + (r.Uint64() % (end - begin + 1)) 145 } 146 147 // biasedRand returns a random int in range [0..n), 148 // probability of n-1 is k times higher than probability of 0. 149 func (r *randGen) biasedRand(n, k int) int { 150 nf, kf := float64(n), float64(k) 151 rf := nf * (kf/2 + 1) * r.Float64() 152 bf := (-1 + math.Sqrt(1+2*kf*rf/nf)) * nf / kf 153 return int(bf) 154 } 155 156 func (r *randGen) randArrayLen() uint64 { 157 const maxLen = 10 158 // biasedRand produces: 10, 9, ..., 1, 0, 159 // we want: 1, 2, ..., 9, 10, 0 160 return uint64(maxLen-r.biasedRand(maxLen+1, 10)+1) % (maxLen + 1) 161 } 162 163 func (r *randGen) randBufLen() (n uint64) { 164 switch { 165 case r.nOutOf(50, 56): 166 n = r.rand(256) 167 case r.nOutOf(5, 6): 168 n = 4 << 10 169 } 170 return 171 } 172 173 func (r *randGen) randPageCount() (n uint64) { 174 switch { 175 case r.nOutOf(100, 106): 176 n = r.rand(4) + 1 177 case r.nOutOf(5, 6): 178 n = r.rand(20) + 1 179 default: 180 n = (r.rand(3) + 1) * r.target.NumPages / 4 181 } 182 return 183 } 184 185 // Change a flag value or generate a new one. 186 // If you are changing this function, run TestFlags and examine effect of results. 187 func (r *randGen) flags(vv []uint64, bitmask bool, oldVal uint64) uint64 { 188 // Get these simpler cases out of the way first. 189 // Once in a while we want to return completely random values, 190 // or 0 which is frequently special. 191 if r.oneOf(100) { 192 return r.rand64() 193 } 194 if r.oneOf(50) { 195 return 0 196 } 197 if !bitmask && oldVal != 0 && r.oneOf(100) { 198 // Slightly increment/decrement the old value. 199 // This is especially important during mutation when len(vv) == 1, 200 // otherwise in that case we produce almost no randomness 201 // (the value is always mutated to 0). 202 inc := uint64(1) 203 if r.bin() { 204 inc = ^uint64(0) 205 } 206 v := oldVal + inc 207 for r.bin() { 208 v += inc 209 } 210 return v 211 } 212 if len(vv) == 1 { 213 // This usually means that value or 0, 214 // at least that's our best (and only) bet. 215 if r.bin() { 216 return 0 217 } 218 return vv[0] 219 } 220 if !bitmask && !r.oneOf(10) { 221 // Enumeration, so just choose one of the values. 222 return vv[r.rand(len(vv))] 223 } 224 if r.oneOf(len(vv) + 4) { 225 return 0 226 } 227 // Flip rand bits. Do this for non-bitmask sometimes 228 // because we may have detected bitmask incorrectly for complex cases 229 // (e.g. part of the vlaue is bitmask and another is not). 230 v := oldVal 231 if v != 0 && r.oneOf(10) { 232 v = 0 // Ignore the old value sometimes. 233 } 234 // We don't want to return 0 here, because we already given 0 235 // fixed probability above (otherwise we get 0 too frequently). 236 // Note: this loop can hang if all values are equal to 0. We don't generate such flags in the compiler now, 237 // but it used to hang occasionally, so we keep the try < 10 logic b/c we don't have a local check for values. 238 for try := 0; try < 10 && (v == 0 || r.nOutOf(2, 3)); try++ { 239 flag := vv[r.rand(len(vv))] 240 if r.oneOf(20) { 241 // Try choosing adjacent bit values in case we forgot 242 // to add all relevant flags to the descriptions. 243 if r.bin() { 244 flag >>= 1 245 } else { 246 flag <<= 1 247 } 248 } 249 v ^= flag 250 } 251 return v 252 } 253 254 func (r *randGen) filename(s *state, typ *BufferType) string { 255 fn := r.filenameImpl(s) 256 if fn != "" && fn[len(fn)-1] == 0 { 257 panic(fmt.Sprintf("zero-terminated filename: %q", fn)) 258 } 259 if escapingFilename(fn) { 260 panic(fmt.Sprintf("sandbox escaping file name %q, s.files are %v", fn, s.files)) 261 } 262 if !typ.Varlen() { 263 size := typ.Size() 264 if uint64(len(fn)) < size { 265 fn += string(make([]byte, size-uint64(len(fn)))) 266 } 267 fn = fn[:size] 268 } else if !typ.NoZ { 269 fn += "\x00" 270 } 271 return fn 272 } 273 274 func escapingFilename(file string) bool { 275 file = filepath.Clean(file) 276 return len(file) >= 1 && file[0] == '/' || 277 len(file) >= 2 && file[0] == '.' && file[1] == '.' 278 } 279 280 var specialFiles = []string{"", "."} 281 282 const specialFileLenPad = "a" 283 284 func (r *randGen) filenameImpl(s *state) string { 285 if r.oneOf(100) { 286 return specialFiles[r.Intn(len(specialFiles))] 287 } 288 if len(s.files) == 0 || r.oneOf(10) { 289 // Generate a new name. 290 dir := "." 291 if r.oneOf(2) && len(s.files) != 0 { 292 dir = r.randFromMap(s.files) 293 if dir != "" && dir[len(dir)-1] == 0 { 294 dir = dir[:len(dir)-1] 295 } 296 if r.oneOf(10) && filepath.Clean(dir)[0] != '.' { 297 dir += "/.." 298 } 299 } 300 for i := 0; ; i++ { 301 f := fmt.Sprintf("%v/file%v", dir, i) 302 if r.oneOf(100) { 303 // Make file name very long using target.SpecialFileLenghts consts. 304 // Add/subtract some small const to account for our file name prefix 305 // and potential kernel off-by-one's. 306 fileLen := r.randFilenameLength() 307 if add := fileLen - len(f); add > 0 { 308 f += strings.Repeat(specialFileLenPad, add) 309 } 310 } 311 if !s.files[f] { 312 return f 313 } 314 } 315 } 316 return r.randFromMap(s.files) 317 } 318 319 func (r *randGen) randFilenameLength() int { 320 off := r.biasedRand(10, 5) 321 if r.bin() { 322 off = -off 323 } 324 lens := r.target.SpecialFileLenghts 325 res := lens[r.Intn(len(lens))] + off 326 if res < 0 { 327 res = 0 328 } 329 return res 330 } 331 332 func (r *randGen) randFromMap(m map[string]bool) string { 333 files := make([]string, 0, len(m)) 334 for f := range m { 335 files = append(files, f) 336 } 337 sort.Strings(files) 338 return files[r.Intn(len(files))] 339 } 340 341 func (r *randGen) randString(s *state, t *BufferType) []byte { 342 if len(t.Values) != 0 { 343 return []byte(t.Values[r.Intn(len(t.Values))]) 344 } 345 if len(s.strings) != 0 && r.bin() { 346 // Return an existing string. 347 // TODO(dvyukov): make s.strings indexed by string SubKind. 348 return []byte(r.randFromMap(s.strings)) 349 } 350 punct := []byte{'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '+', '\\', 351 '/', ':', '.', ',', '-', '\'', '[', ']', '{', '}'} 352 buf := new(bytes.Buffer) 353 for r.nOutOf(3, 4) { 354 if r.nOutOf(10, 11) { 355 buf.Write([]byte{punct[r.Intn(len(punct))]}) 356 } else { 357 buf.Write([]byte{byte(r.Intn(256))}) 358 } 359 } 360 if r.oneOf(100) == t.NoZ { 361 buf.Write([]byte{0}) 362 } 363 return buf.Bytes() 364 } 365 366 func (r *randGen) allocAddr(s *state, typ Type, dir Dir, size uint64, data Arg) *PointerArg { 367 return MakePointerArg(typ, dir, s.ma.alloc(r, size, data.Type().Alignment()), data) 368 } 369 370 func (r *randGen) allocVMA(s *state, typ Type, dir Dir, numPages uint64) *PointerArg { 371 page := s.va.alloc(r, numPages) 372 return MakeVmaPointerArg(typ, dir, page*r.target.PageSize, numPages*r.target.PageSize) 373 } 374 375 func (r *randGen) createResource(s *state, res *ResourceType, dir Dir) (Arg, []*Call) { 376 if !r.inGenerateResource { 377 panic("inGenerateResource is not set") 378 } 379 kind := res.Desc.Name 380 // Find calls that produce the necessary resources. 381 ctors := r.enabledCtors(s, kind) 382 // We may have no resources, but still be in createResource due to ANYRES. 383 if len(r.target.resourceMap) != 0 && r.oneOf(1000) { 384 // Spoof resource subkind. 385 var all []string 386 for kind1 := range r.target.resourceMap { 387 if r.target.isCompatibleResource(res.Desc.Kind[0], kind1) { 388 all = append(all, kind1) 389 } 390 } 391 if len(all) == 0 { 392 panic(fmt.Sprintf("got no spoof resources for %v in %v/%v", 393 kind, r.target.OS, r.target.Arch)) 394 } 395 sort.Strings(all) 396 kind1 := all[r.Intn(len(all))] 397 ctors1 := r.enabledCtors(s, kind1) 398 if len(ctors1) != 0 { 399 // Don't use the resource for which we don't have any ctors. 400 // It's fine per-se because below we just return nil in such case. 401 // But in TestCreateResource tests we want to ensure that we don't fail 402 // to create non-optional resources, and if we spoof a non-optional 403 // resource with ctors with a optional resource w/o ctors, then that check will fail. 404 kind, ctors = kind1, ctors1 405 } 406 } 407 if len(ctors) == 0 { 408 // We may not have any constructors for optional input resources because we don't disable 409 // syscalls based on optional inputs resources w/o ctors in TransitivelyEnabledCalls. 410 return nil, nil 411 } 412 // Now we have a set of candidate calls that can create the necessary resource. 413 // Generate one of them. 414 var meta *Syscall 415 // Prefer precise constructors. 416 var precise []*Syscall 417 for _, info := range ctors { 418 if info.Precise { 419 precise = append(precise, info.Call) 420 } 421 } 422 if len(precise) > 0 { 423 // If the argument is optional, it's not guaranteed that there'd be a 424 // precise constructor. 425 meta = precise[r.Intn(len(precise))] 426 } 427 if meta == nil || r.oneOf(3) { 428 // Sometimes just take a random one. 429 meta = ctors[r.Intn(len(ctors))].Call 430 } 431 432 calls := r.generateParticularCall(s, meta) 433 s1 := newState(r.target, s.ct, nil) 434 s1.analyze(calls[len(calls)-1]) 435 // Now see if we have what we want. 436 var allres []*ResultArg 437 for kind1, res1 := range s1.resources { 438 if r.target.isCompatibleResource(kind, kind1) { 439 allres = append(allres, res1...) 440 } 441 } 442 sort.SliceStable(allres, func(i, j int) bool { 443 return allres[i].Type().Name() < allres[j].Type().Name() 444 }) 445 if len(allres) == 0 { 446 panic(fmt.Sprintf("failed to create a resource %v (%v) with %v", 447 res.Desc.Kind[0], kind, meta.Name)) 448 } 449 arg := MakeResultArg(res, dir, allres[r.Intn(len(allres))], 0) 450 return arg, calls 451 } 452 453 func (r *randGen) enabledCtors(s *state, kind string) []ResourceCtor { 454 var ret []ResourceCtor 455 for _, info := range r.target.resourceCtors[kind] { 456 if s.ct.Generatable(info.Call.ID) { 457 ret = append(ret, info) 458 } 459 } 460 return ret 461 } 462 463 func (r *randGen) generateText(kind TextKind) []byte { 464 switch kind { 465 case TextTarget: 466 if cfg := createTargetIfuzzConfig(r.target); cfg != nil { 467 return ifuzz.Generate(cfg, r.Rand) 468 } 469 text := make([]byte, 50) 470 for i := range text { 471 text[i] = byte(r.Intn(256)) 472 } 473 return text 474 default: 475 cfg := createIfuzzConfig(kind) 476 return ifuzz.Generate(cfg, r.Rand) 477 } 478 } 479 480 func (r *randGen) mutateText(kind TextKind, text []byte) []byte { 481 switch kind { 482 case TextTarget: 483 if cfg := createTargetIfuzzConfig(r.target); cfg != nil { 484 return ifuzz.Mutate(cfg, r.Rand, text) 485 } 486 return mutateData(r, text, 40, 60) 487 default: 488 cfg := createIfuzzConfig(kind) 489 return ifuzz.Mutate(cfg, r.Rand, text) 490 } 491 } 492 493 func createTargetIfuzzConfig(target *Target) *ifuzz.Config { 494 cfg := &ifuzz.Config{ 495 Len: 10, 496 Priv: false, 497 Exec: true, 498 MemRegions: []ifuzz.MemRegion{ 499 {Start: target.DataOffset, Size: target.NumPages * target.PageSize}, 500 }, 501 } 502 for _, p := range target.SpecialPointers { 503 cfg.MemRegions = append(cfg.MemRegions, ifuzz.MemRegion{ 504 Start: p & ^target.PageSize, Size: p & ^target.PageSize + target.PageSize, 505 }) 506 } 507 switch target.Arch { 508 case "amd64": 509 cfg.Mode = ifuzz.ModeLong64 510 cfg.Arch = ifuzz.ArchX86 511 case "386": 512 cfg.Mode = ifuzz.ModeProt32 513 cfg.Arch = ifuzz.ArchX86 514 case "ppc64": 515 cfg.Mode = ifuzz.ModeLong64 516 cfg.Arch = ifuzz.ArchPowerPC 517 case "arm64": 518 cfg.Mode = ifuzz.ModeLong64 519 cfg.Arch = ifuzz.ArchArm64 520 default: 521 return nil 522 } 523 return cfg 524 } 525 526 func createIfuzzConfig(kind TextKind) *ifuzz.Config { 527 cfg := &ifuzz.Config{ 528 Len: 10, 529 Priv: true, 530 Exec: true, 531 MemRegions: []ifuzz.MemRegion{ 532 {Start: 0 << 12, Size: 1 << 12}, 533 {Start: 1 << 12, Size: 1 << 12}, 534 {Start: 2 << 12, Size: 1 << 12}, 535 {Start: 3 << 12, Size: 1 << 12}, 536 {Start: 4 << 12, Size: 1 << 12}, 537 {Start: 5 << 12, Size: 1 << 12}, 538 {Start: 6 << 12, Size: 1 << 12}, 539 {Start: 7 << 12, Size: 1 << 12}, 540 {Start: 8 << 12, Size: 1 << 12}, 541 {Start: 9 << 12, Size: 1 << 12}, 542 {Start: 0xfec00000, Size: 0x100}, // ioapic 543 }, 544 } 545 switch kind { 546 case TextX86Real: 547 cfg.Mode = ifuzz.ModeReal16 548 cfg.Arch = ifuzz.ArchX86 549 case TextX86bit16: 550 cfg.Mode = ifuzz.ModeProt16 551 cfg.Arch = ifuzz.ArchX86 552 case TextX86bit32: 553 cfg.Mode = ifuzz.ModeProt32 554 cfg.Arch = ifuzz.ArchX86 555 case TextX86bit64: 556 cfg.Mode = ifuzz.ModeLong64 557 cfg.Arch = ifuzz.ArchX86 558 case TextPpc64: 559 cfg.Mode = ifuzz.ModeLong64 560 cfg.Arch = ifuzz.ArchPowerPC 561 case TextArm64: 562 cfg.Mode = ifuzz.ModeLong64 563 cfg.Arch = ifuzz.ArchArm64 564 default: 565 panic(fmt.Sprintf("unknown text kind: %v", kind)) 566 } 567 return cfg 568 } 569 570 // nOutOf returns true n out of outOf times. 571 func (r *randGen) nOutOf(n, outOf int) bool { 572 if n <= 0 || n >= outOf { 573 panic("bad probability") 574 } 575 v := r.Intn(outOf) 576 return v < n 577 } 578 579 func (r *randGen) generateCall(s *state, p *Prog, insertionPoint int) []*Call { 580 biasCall := -1 581 if insertionPoint > 0 { 582 // Choosing the base call is based on the insertion point of the new calls sequence. 583 insertionCall := p.Calls[r.Intn(insertionPoint)].Meta 584 if !insertionCall.Attrs.NoGenerate { 585 // We must be careful not to bias towards a non-generatable call. 586 biasCall = insertionCall.ID 587 } 588 } 589 idx := s.ct.choose(r.Rand, biasCall) 590 meta := r.target.Syscalls[idx] 591 return r.generateParticularCall(s, meta) 592 } 593 594 func (r *randGen) generateParticularCall(s *state, meta *Syscall) (calls []*Call) { 595 if meta.Attrs.Disabled { 596 panic(fmt.Sprintf("generating disabled call %v", meta.Name)) 597 } 598 if meta.Attrs.NoGenerate { 599 panic(fmt.Sprintf("generating no_generate call: %v", meta.Name)) 600 } 601 c := MakeCall(meta, nil) 602 c.Args, calls = r.generateArgs(s, meta.Args, DirIn) 603 moreCalls, _ := r.patchConditionalFields(c, s) 604 r.target.assignSizesCall(c) 605 return append(append(calls, moreCalls...), c) 606 } 607 608 // GenerateAllSyzProg generates a program that contains all pseudo syz_ calls for testing. 609 func (target *Target) GenerateAllSyzProg(rs rand.Source) *Prog { 610 p := &Prog{ 611 Target: target, 612 } 613 r := newRand(target, rs) 614 s := newState(target, target.DefaultChoiceTable(), nil) 615 for _, meta := range target.PseudoSyscalls() { 616 calls := r.generateParticularCall(s, meta) 617 for _, c := range calls { 618 s.analyze(c) 619 p.Calls = append(p.Calls, c) 620 } 621 } 622 if err := p.validate(); err != nil { 623 panic(err) 624 } 625 return p 626 } 627 628 // PseudoSyscalls selects one *Syscall for each pseudosyscall. 629 func (target *Target) PseudoSyscalls() []*Syscall { 630 handled := make(map[string]bool) 631 var ret []*Syscall 632 for _, meta := range target.Syscalls { 633 if !strings.HasPrefix(meta.CallName, "syz_") || 634 handled[meta.CallName] || 635 meta.Attrs.Disabled || 636 meta.Attrs.NoGenerate { 637 continue 638 } 639 ret = append(ret, meta) 640 handled[meta.CallName] = true 641 } 642 return ret 643 } 644 645 // GenSampleProg generates a single sample program for the call. 646 func (target *Target) GenSampleProg(meta *Syscall, rs rand.Source) *Prog { 647 r := newRand(target, rs) 648 s := newState(target, target.DefaultChoiceTable(), nil) 649 p := &Prog{ 650 Target: target, 651 } 652 for _, c := range r.generateParticularCall(s, meta) { 653 s.analyze(c) 654 p.Calls = append(p.Calls, c) 655 } 656 if err := p.validate(); err != nil { 657 panic(err) 658 } 659 return p 660 } 661 662 // DataMmapProg creates program that maps data segment. 663 // Also used for testing as the simplest program. 664 func (target *Target) DataMmapProg() *Prog { 665 return &Prog{ 666 Target: target, 667 Calls: target.MakeDataMmap(), 668 isUnsafe: true, 669 } 670 } 671 672 func (r *randGen) generateArgs(s *state, fields []Field, dir Dir) ([]Arg, []*Call) { 673 var calls []*Call 674 args := make([]Arg, len(fields)) 675 676 // Generate all args. Size args have the default value 0 for now. 677 for i, field := range fields { 678 arg, calls1 := r.generateArg(s, field.Type, field.Dir(dir)) 679 if arg == nil { 680 panic(fmt.Sprintf("generated arg is nil for field '%v', fields: %+v", field.Type.Name(), fields)) 681 } 682 args[i] = arg 683 calls = append(calls, calls1...) 684 } 685 686 return args, calls 687 } 688 689 func (r *randGen) generateArg(s *state, typ Type, dir Dir) (arg Arg, calls []*Call) { 690 return r.generateArgImpl(s, typ, dir, false) 691 } 692 693 func (r *randGen) generateArgImpl(s *state, typ Type, dir Dir, ignoreSpecial bool) (arg Arg, calls []*Call) { 694 if dir == DirOut { 695 // No need to generate something interesting for output scalar arguments. 696 // But we still need to generate the argument itself so that it can be referenced 697 // in subsequent calls. For the same reason we do generate pointer/array/struct 698 // output arguments (their elements can be referenced in subsequent calls). 699 switch typ.(type) { 700 case *IntType, *FlagsType, *ConstType, *ProcType, *VmaType, *ResourceType: 701 return typ.DefaultArg(dir), nil 702 } 703 } 704 705 if typ.Optional() && r.oneOf(5) { 706 if res, ok := typ.(*ResourceType); ok { 707 v := res.Desc.Values[r.Intn(len(res.Desc.Values))] 708 return MakeResultArg(typ, dir, nil, v), nil 709 } 710 return typ.DefaultArg(dir), nil 711 } 712 713 // Allow infinite recursion for optional pointers. 714 if pt, ok := typ.(*PtrType); ok && typ.Optional() { 715 switch pt.Elem.(type) { 716 case *StructType, *ArrayType, *UnionType: 717 name := pt.Elem.Name() 718 r.recDepth[name]++ 719 defer func() { 720 r.recDepth[name]-- 721 if r.recDepth[name] == 0 { 722 delete(r.recDepth, name) 723 } 724 }() 725 if r.recDepth[name] >= 3 { 726 return MakeSpecialPointerArg(typ, dir, 0), nil 727 } 728 } 729 } 730 731 if !ignoreSpecial && dir != DirOut { 732 switch typ.(type) { 733 case *StructType, *UnionType: 734 if gen := r.target.SpecialTypes[typ.Name()]; gen != nil { 735 return gen(&Gen{r, s}, typ, dir, nil) 736 } 737 } 738 } 739 740 return typ.generate(r, s, dir) 741 } 742 743 func (a *ResourceType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 744 canRecurse := false 745 if !r.inGenerateResource { 746 // Don't allow recursion for resourceCentric/createResource. 747 // That can lead to generation of huge programs and may be very slow 748 // (esp. if we are generating some failing attempts in createResource already). 749 r.inGenerateResource = true 750 defer func() { r.inGenerateResource = false }() 751 canRecurse = true 752 } 753 if canRecurse && r.nOutOf(8, 10) || 754 !canRecurse && r.nOutOf(19, 20) { 755 arg = r.existingResource(s, a, dir) 756 if arg != nil { 757 return 758 } 759 } 760 if canRecurse { 761 if r.oneOf(4) { 762 arg, calls = r.resourceCentric(s, a, dir) 763 if arg != nil { 764 return 765 } 766 } 767 if r.nOutOf(4, 5) { 768 // If we could not reuse a resource, let's prefer resource creation over 769 // random int substitution. 770 arg, calls = r.createResource(s, a, dir) 771 if arg != nil { 772 return 773 } 774 } 775 } 776 special := a.SpecialValues() 777 arg = MakeResultArg(a, dir, nil, special[r.Intn(len(special))]) 778 return 779 } 780 781 func (a *BufferType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 782 switch a.Kind { 783 case BufferBlobRand, BufferBlobRange: 784 sz := r.randBufLen() 785 if a.Kind == BufferBlobRange { 786 sz = r.randRange(a.RangeBegin, a.RangeEnd) 787 } 788 if dir == DirOut { 789 return MakeOutDataArg(a, dir, sz), nil 790 } 791 data := make([]byte, sz) 792 for i := range data { 793 data[i] = byte(r.Intn(256)) 794 } 795 return MakeDataArg(a, dir, data), nil 796 case BufferString: 797 data := r.randString(s, a) 798 if dir == DirOut { 799 return MakeOutDataArg(a, dir, uint64(len(data))), nil 800 } 801 return MakeDataArg(a, dir, data), nil 802 case BufferFilename: 803 if dir == DirOut { 804 var sz uint64 805 switch { 806 case !a.Varlen(): 807 sz = a.Size() 808 case r.nOutOf(1, 3): 809 sz = r.rand(100) 810 default: 811 sz = uint64(r.randFilenameLength()) 812 } 813 return MakeOutDataArg(a, dir, sz), nil 814 } 815 return MakeDataArg(a, dir, []byte(r.filename(s, a))), nil 816 case BufferGlob: 817 return MakeDataArg(a, dir, r.randString(s, a)), nil 818 case BufferText: 819 if dir == DirOut { 820 return MakeOutDataArg(a, dir, uint64(r.Intn(100))), nil 821 } 822 return MakeDataArg(a, dir, r.generateText(a.Text)), nil 823 case BufferCompressed: 824 panic(fmt.Sprintf("can't generate compressed type %v", a)) 825 default: 826 panic("unknown buffer kind") 827 } 828 } 829 830 func (a *VmaType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 831 npages := r.randPageCount() 832 if a.RangeBegin != 0 || a.RangeEnd != 0 { 833 npages = a.RangeBegin + uint64(r.Intn(int(a.RangeEnd-a.RangeBegin+1))) 834 } 835 return r.allocVMA(s, a, dir, npages), nil 836 } 837 838 func (a *FlagsType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 839 return MakeConstArg(a, dir, r.flags(a.Vals, a.BitMask, 0)), nil 840 } 841 842 func (a *ConstType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 843 return MakeConstArg(a, dir, a.Val), nil 844 } 845 846 func (a *IntType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 847 bits := a.TypeBitSize() 848 v := r.randInt(bits) 849 switch a.Kind { 850 case IntRange: 851 v = r.randRangeInt(a.RangeBegin, a.RangeEnd, bits, a.Align) 852 } 853 return MakeConstArg(a, dir, v), nil 854 } 855 856 func (a *ProcType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 857 return MakeConstArg(a, dir, r.rand(int(a.ValuesPerProc))), nil 858 } 859 860 func (a *ArrayType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 861 var count uint64 862 switch a.Kind { 863 case ArrayRandLen: 864 count = r.randArrayLen() 865 case ArrayRangeLen: 866 count = r.randRange(a.RangeBegin, a.RangeEnd) 867 } 868 // The resource we are trying to generate may be in the array elements, so create at least 1. 869 if r.inGenerateResource && count == 0 { 870 count = 1 871 } 872 var inner []Arg 873 for i := uint64(0); i < count; i++ { 874 arg1, calls1 := r.generateArg(s, a.Elem, dir) 875 inner = append(inner, arg1) 876 calls = append(calls, calls1...) 877 } 878 return MakeGroupArg(a, dir, inner), calls 879 } 880 881 func (a *StructType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 882 args, calls := r.generateArgs(s, a.Fields, dir) 883 group := MakeGroupArg(a, dir, args) 884 return group, calls 885 } 886 887 func (a *UnionType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 888 if a.isConditional() { 889 // Conditions may reference other fields that may not have already 890 // been generated. We'll fill them in later. 891 return a.DefaultArg(dir), nil 892 } 893 index := r.Intn(len(a.Fields)) 894 optType, optDir := a.Fields[index].Type, a.Fields[index].Dir(dir) 895 opt, calls := r.generateArg(s, optType, optDir) 896 return MakeUnionArg(a, dir, opt, index), calls 897 } 898 899 func (a *PtrType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 900 // The resource we are trying to generate may be in the pointer, 901 // so don't try to create an empty special pointer during resource generation. 902 if !r.inGenerateResource && r.oneOf(1000) { 903 index := r.rand(len(r.target.SpecialPointers)) 904 return MakeSpecialPointerArg(a, dir, index), nil 905 } 906 inner, calls := r.generateArg(s, a.Elem, a.ElemDir) 907 arg = r.allocAddr(s, a, dir, inner.Size(), inner) 908 return arg, calls 909 } 910 911 func (a *LenType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 912 // Updated later in assignSizesCall. 913 return MakeConstArg(a, dir, 0), nil 914 } 915 916 func (a *CsumType) generate(r *randGen, s *state, dir Dir) (arg Arg, calls []*Call) { 917 // Filled at runtime by executor. 918 return MakeConstArg(a, dir, 0), nil 919 } 920 921 func (r *randGen) existingResource(s *state, res *ResourceType, dir Dir) Arg { 922 alltypes := make([][]*ResultArg, 0, len(s.resources)) 923 for _, res1 := range s.resources { 924 alltypes = append(alltypes, res1) 925 } 926 sort.Slice(alltypes, func(i, j int) bool { 927 return alltypes[i][0].Type().Name() < alltypes[j][0].Type().Name() 928 }) 929 var allres []*ResultArg 930 for _, res1 := range alltypes { 931 name1 := res1[0].Type().Name() 932 if r.target.isCompatibleResource(res.Desc.Name, name1) || 933 r.oneOf(50) && r.target.isCompatibleResource(res.Desc.Kind[0], name1) { 934 allres = append(allres, res1...) 935 } 936 } 937 if len(allres) == 0 { 938 return nil 939 } 940 return MakeResultArg(res, dir, allres[r.Intn(len(allres))], 0) 941 } 942 943 // Finds a compatible resource with the type `t` and the calls that initialize that resource. 944 func (r *randGen) resourceCentric(s *state, t *ResourceType, dir Dir) (arg Arg, calls []*Call) { 945 var p *Prog 946 var resource *ResultArg 947 for _, idx := range r.Perm(len(s.corpus)) { 948 corpusProg := s.corpus[idx] 949 resources := getCompatibleResources(corpusProg, t.TypeName, r) 950 if len(resources) == 0 { 951 continue 952 } 953 argMap := make(map[*ResultArg]*ResultArg) 954 p = corpusProg.cloneWithMap(argMap) 955 resource = argMap[resources[r.Intn(len(resources))]] 956 break 957 } 958 959 // No compatible resource was found. 960 if resource == nil { 961 return nil, nil 962 } 963 964 // Set that stores the resources that appear in the same calls with the selected resource. 965 relatedRes := map[*ResultArg]bool{resource: true} 966 967 // Remove unrelated calls from the program. 968 for idx := len(p.Calls) - 1; idx >= 0; idx-- { 969 includeCall := false 970 var newResources []*ResultArg 971 ForeachArg(p.Calls[idx], func(arg Arg, _ *ArgCtx) { 972 if a, ok := arg.(*ResultArg); ok { 973 if a.Res != nil && !relatedRes[a.Res] { 974 newResources = append(newResources, a.Res) 975 } 976 if relatedRes[a] || relatedRes[a.Res] { 977 includeCall = true 978 } 979 } 980 }) 981 if !includeCall { 982 p.RemoveCall(idx) 983 } else { 984 for _, res := range newResources { 985 relatedRes[res] = true 986 } 987 } 988 } 989 990 // Selects a biased random length of the returned calls (more calls could offer more 991 // interesting programs). The values returned (n = len(calls): n, n-1, ..., 2. 992 biasedLen := 2 + r.biasedRand(len(calls)-1, 10) 993 994 // Removes the references that are not used anymore. 995 for i := biasedLen; i < len(calls); i++ { 996 p.RemoveCall(i) 997 } 998 999 return MakeResultArg(t, dir, resource, 0), p.Calls 1000 } 1001 1002 func getCompatibleResources(p *Prog, resourceType string, r *randGen) (resources []*ResultArg) { 1003 for _, c := range p.Calls { 1004 ForeachArg(c, func(arg Arg, _ *ArgCtx) { 1005 // Collect only initialized resources (the ones that are already used in other calls). 1006 a, ok := arg.(*ResultArg) 1007 if !ok || len(a.uses) == 0 || a.Dir() != DirOut { 1008 return 1009 } 1010 if !r.target.isCompatibleResource(resourceType, a.Type().Name()) { 1011 return 1012 } 1013 resources = append(resources, a) 1014 }) 1015 } 1016 return resources 1017 }