1// Copyright 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 4package repro 5 6import ( 7 "bytes" 8 "fmt" 9 "os" 10 "sort" 11 "sync" 12 "time" 13 14 "github.com/google/syzkaller/pkg/csource" 15 instancePkg "github.com/google/syzkaller/pkg/instance" 16 "github.com/google/syzkaller/pkg/log" 17 "github.com/google/syzkaller/pkg/mgrconfig" 18 "github.com/google/syzkaller/pkg/osutil" 19 "github.com/google/syzkaller/pkg/report" 20 "github.com/google/syzkaller/prog" 21 "github.com/google/syzkaller/vm" 22) 23 24type Result struct { 25 Prog *prog.Prog 26 Duration time.Duration 27 Opts csource.Options 28 CRepro bool 29 // Information about the final (non-symbolized) crash that we reproduced. 30 // Can be different from what we started reproducing. 31 Report *report.Report 32} 33 34type Stats struct { 35 Log []byte 36 ExtractProgTime time.Duration 37 MinimizeProgTime time.Duration 38 SimplifyProgTime time.Duration 39 ExtractCTime time.Duration 40 SimplifyCTime time.Duration 41} 42 43type context struct { 44 cfg *mgrconfig.Config 45 reporter report.Reporter 46 crashTitle string 47 instances chan *instance 48 bootRequests chan int 49 stats *Stats 50 report *report.Report 51} 52 53type instance struct { 54 *vm.Instance 55 index int 56 execprogBin string 57 executorBin string 58} 59 60func Run(crashLog []byte, cfg *mgrconfig.Config, reporter report.Reporter, vmPool *vm.Pool, 61 vmIndexes []int) (*Result, *Stats, error) { 62 if len(vmIndexes) == 0 { 63 return nil, nil, fmt.Errorf("no VMs provided") 64 } 65 target, err := prog.GetTarget(cfg.TargetOS, cfg.TargetArch) 66 if err != nil { 67 return nil, nil, err 68 } 69 entries := target.ParseLog(crashLog) 70 if len(entries) == 0 { 71 return nil, nil, fmt.Errorf("crash log does not contain any programs") 72 } 73 crashStart := len(crashLog) // assuming VM hanged 74 crashTitle := "hang" 75 if rep := reporter.Parse(crashLog); rep != nil { 76 crashStart = rep.StartPos 77 crashTitle = rep.Title 78 } 79 80 ctx := &context{ 81 cfg: cfg, 82 reporter: reporter, 83 crashTitle: crashTitle, 84 instances: make(chan *instance, len(vmIndexes)), 85 bootRequests: make(chan int, len(vmIndexes)), 86 stats: new(Stats), 87 } 88 ctx.reproLog(0, "%v programs, %v VMs", len(entries), len(vmIndexes)) 89 var wg sync.WaitGroup 90 wg.Add(len(vmIndexes)) 91 for _, vmIndex := range vmIndexes { 92 ctx.bootRequests <- vmIndex 93 go func() { 94 defer wg.Done() 95 for vmIndex := range ctx.bootRequests { 96 var inst *instance 97 maxTry := 3 98 for try := 0; try < maxTry; try++ { 99 select { 100 case <-vm.Shutdown: 101 try = maxTry 102 continue 103 default: 104 } 105 vmInst, err := vmPool.Create(vmIndex) 106 if err != nil { 107 ctx.reproLog(0, "failed to create VM: %v", err) 108 time.Sleep(10 * time.Second) 109 continue 110 111 } 112 execprogBin, err := vmInst.Copy(cfg.SyzExecprogBin) 113 if err != nil { 114 ctx.reproLog(0, "failed to copy to VM: %v", err) 115 vmInst.Close() 116 time.Sleep(10 * time.Second) 117 continue 118 } 119 executorBin, err := vmInst.Copy(cfg.SyzExecutorBin) 120 if err != nil { 121 ctx.reproLog(0, "failed to copy to VM: %v", err) 122 vmInst.Close() 123 time.Sleep(10 * time.Second) 124 continue 125 } 126 inst = &instance{ 127 Instance: vmInst, 128 index: vmIndex, 129 execprogBin: execprogBin, 130 executorBin: executorBin, 131 } 132 break 133 } 134 if inst == nil { 135 break 136 } 137 ctx.instances <- inst 138 } 139 }() 140 } 141 go func() { 142 wg.Wait() 143 close(ctx.instances) 144 }() 145 146 res, err := ctx.repro(entries, crashStart) 147 if err != nil { 148 return nil, nil, err 149 } 150 if res != nil { 151 ctx.reproLog(3, "repro crashed as (corrupted=%v):\n%s", 152 ctx.report.Corrupted, ctx.report.Report) 153 // Try to rerun the repro if the report is corrupted. 154 for attempts := 0; ctx.report.Corrupted && attempts < 3; attempts++ { 155 ctx.reproLog(3, "report is corrupted, running repro again") 156 if res.CRepro { 157 _, err = ctx.testCProg(res.Prog, res.Duration, res.Opts) 158 } else { 159 _, err = ctx.testProg(res.Prog, res.Duration, res.Opts) 160 } 161 if err != nil { 162 return nil, nil, err 163 } 164 } 165 ctx.reproLog(3, "final repro crashed as (corrupted=%v):\n%s", 166 ctx.report.Corrupted, ctx.report.Report) 167 res.Report = ctx.report 168 } 169 170 close(ctx.bootRequests) 171 for inst := range ctx.instances { 172 inst.Close() 173 } 174 return res, ctx.stats, nil 175} 176 177func (ctx *context) repro(entries []*prog.LogEntry, crashStart int) (*Result, error) { 178 // Cut programs that were executed after crash. 179 for i, ent := range entries { 180 if ent.Start > crashStart { 181 entries = entries[:i] 182 break 183 } 184 } 185 186 reproStart := time.Now() 187 defer func() { 188 ctx.reproLog(3, "reproducing took %s", time.Since(reproStart)) 189 }() 190 191 res, err := ctx.extractProg(entries) 192 if err != nil { 193 return nil, err 194 } 195 if res == nil { 196 return nil, nil 197 } 198 defer func() { 199 if res != nil { 200 res.Opts.Repro = false 201 } 202 }() 203 res, err = ctx.minimizeProg(res) 204 if err != nil { 205 return nil, err 206 } 207 208 // Try extracting C repro without simplifying options first. 209 res, err = ctx.extractC(res) 210 if err != nil { 211 return nil, err 212 } 213 214 // Simplify options and try extracting C repro. 215 if !res.CRepro { 216 res, err = ctx.simplifyProg(res) 217 if err != nil { 218 return nil, err 219 } 220 } 221 222 // Simplify C related options. 223 if res.CRepro { 224 res, err = ctx.simplifyC(res) 225 if err != nil { 226 return nil, err 227 } 228 } 229 230 return res, nil 231} 232 233func (ctx *context) extractProg(entries []*prog.LogEntry) (*Result, error) { 234 ctx.reproLog(2, "extracting reproducer from %v programs", len(entries)) 235 start := time.Now() 236 defer func() { 237 ctx.stats.ExtractProgTime = time.Since(start) 238 }() 239 240 // Extract last program on every proc. 241 procs := make(map[int]int) 242 for i, ent := range entries { 243 procs[ent.Proc] = i 244 } 245 var indices []int 246 for _, idx := range procs { 247 indices = append(indices, idx) 248 } 249 sort.Ints(indices) 250 var lastEntries []*prog.LogEntry 251 for i := len(indices) - 1; i >= 0; i-- { 252 lastEntries = append(lastEntries, entries[indices[i]]) 253 } 254 255 // The shortest duration is 10 seconds to detect simple crashes (i.e. no races and no hangs). 256 // The longest duration is 5 minutes to catch races and hangs. Note that this value must be larger 257 // than hang/no output detection duration in vm.MonitorExecution, which is currently set to 3 mins. 258 timeouts := []time.Duration{10 * time.Second, 1 * time.Minute, 5 * time.Minute} 259 260 for _, timeout := range timeouts { 261 // Execute each program separately to detect simple crashes caused by a single program. 262 // Programs are executed in reverse order, usually the last program is the guilty one. 263 res, err := ctx.extractProgSingle(reverseEntries(lastEntries), timeout) 264 if err != nil { 265 return nil, err 266 } 267 if res != nil { 268 ctx.reproLog(3, "found reproducer with %d syscalls", len(res.Prog.Calls)) 269 return res, nil 270 } 271 272 // Don't try bisecting if there's only one entry. 273 if len(entries) == 1 { 274 continue 275 } 276 277 // Execute all programs and bisect the log to find multiple guilty programs. 278 res, err = ctx.extractProgBisect(reverseEntries(entries), timeout) 279 if err != nil { 280 return nil, err 281 } 282 if res != nil { 283 ctx.reproLog(3, "found reproducer with %d syscalls", len(res.Prog.Calls)) 284 return res, nil 285 } 286 } 287 288 ctx.reproLog(0, "failed to extract reproducer") 289 return nil, nil 290} 291 292func (ctx *context) extractProgSingle(entries []*prog.LogEntry, duration time.Duration) (*Result, error) { 293 ctx.reproLog(3, "single: executing %d programs separately with timeout %s", len(entries), duration) 294 295 opts := csource.DefaultOpts(ctx.cfg) 296 for _, ent := range entries { 297 opts.Fault = ent.Fault 298 opts.FaultCall = ent.FaultCall 299 opts.FaultNth = ent.FaultNth 300 if opts.FaultCall < 0 || opts.FaultCall >= len(ent.P.Calls) { 301 opts.FaultCall = len(ent.P.Calls) - 1 302 } 303 crashed, err := ctx.testProg(ent.P, duration, opts) 304 if err != nil { 305 return nil, err 306 } 307 if crashed { 308 res := &Result{ 309 Prog: ent.P, 310 Duration: duration * 3 / 2, 311 Opts: opts, 312 } 313 ctx.reproLog(3, "single: successfully extracted reproducer") 314 return res, nil 315 } 316 } 317 318 ctx.reproLog(3, "single: failed to extract reproducer") 319 return nil, nil 320} 321 322func (ctx *context) extractProgBisect(entries []*prog.LogEntry, baseDuration time.Duration) (*Result, error) { 323 ctx.reproLog(3, "bisect: bisecting %d programs with base timeout %s", len(entries), baseDuration) 324 325 opts := csource.DefaultOpts(ctx.cfg) 326 duration := func(entries int) time.Duration { 327 return baseDuration + time.Duration((entries/4))*time.Second 328 } 329 330 // Bisect the log to find multiple guilty programs. 331 entries, err := ctx.bisectProgs(entries, func(progs []*prog.LogEntry) (bool, error) { 332 return ctx.testProgs(progs, duration(len(progs)), opts) 333 }) 334 if err != nil { 335 return nil, err 336 } 337 if len(entries) == 0 { 338 return nil, nil 339 } 340 341 // TODO: Minimize each program before concatenation. 342 // TODO: Return multiple programs if concatenation fails. 343 344 ctx.reproLog(3, "bisect: %d programs left: \n\n%s\n", len(entries), encodeEntries(entries)) 345 ctx.reproLog(3, "bisect: trying to concatenate") 346 347 // Concatenate all programs into one. 348 prog := &prog.Prog{ 349 Target: entries[0].P.Target, 350 } 351 for _, entry := range entries { 352 prog.Calls = append(prog.Calls, entry.P.Calls...) 353 } 354 dur := duration(len(entries)) * 3 / 2 355 356 // Execute the program without fault injection. 357 crashed, err := ctx.testProg(prog, dur, opts) 358 if err != nil { 359 return nil, err 360 } 361 if crashed { 362 res := &Result{ 363 Prog: prog, 364 Duration: dur, 365 Opts: opts, 366 } 367 ctx.reproLog(3, "bisect: concatenation succeeded") 368 return res, nil 369 } 370 371 // Try with fault injection. 372 calls := 0 373 for _, entry := range entries { 374 if entry.Fault { 375 opts.FaultCall = calls + entry.FaultCall 376 opts.FaultNth = entry.FaultNth 377 if entry.FaultCall < 0 || entry.FaultCall >= len(entry.P.Calls) { 378 opts.FaultCall = calls + len(entry.P.Calls) - 1 379 } 380 crashed, err := ctx.testProg(prog, dur, opts) 381 if err != nil { 382 return nil, err 383 } 384 if crashed { 385 res := &Result{ 386 Prog: prog, 387 Duration: dur, 388 Opts: opts, 389 } 390 ctx.reproLog(3, "bisect: concatenation succeeded with fault injection") 391 return res, nil 392 } 393 } 394 calls += len(entry.P.Calls) 395 } 396 397 ctx.reproLog(3, "bisect: concatenation failed") 398 return nil, nil 399} 400 401// Minimize calls and arguments. 402func (ctx *context) minimizeProg(res *Result) (*Result, error) { 403 ctx.reproLog(2, "minimizing guilty program") 404 start := time.Now() 405 defer func() { 406 ctx.stats.MinimizeProgTime = time.Since(start) 407 }() 408 409 call := -1 410 if res.Opts.Fault { 411 call = res.Opts.FaultCall 412 } 413 res.Prog, res.Opts.FaultCall = prog.Minimize(res.Prog, call, true, 414 func(p1 *prog.Prog, callIndex int) bool { 415 crashed, err := ctx.testProg(p1, res.Duration, res.Opts) 416 if err != nil { 417 ctx.reproLog(0, "minimization failed with %v", err) 418 return false 419 } 420 return crashed 421 }) 422 423 return res, nil 424} 425 426// Simplify repro options (threaded, collide, sandbox, etc). 427func (ctx *context) simplifyProg(res *Result) (*Result, error) { 428 ctx.reproLog(2, "simplifying guilty program") 429 start := time.Now() 430 defer func() { 431 ctx.stats.SimplifyProgTime = time.Since(start) 432 }() 433 434 for _, simplify := range progSimplifies { 435 opts := res.Opts 436 if !simplify(&opts) { 437 continue 438 } 439 crashed, err := ctx.testProg(res.Prog, res.Duration, opts) 440 if err != nil { 441 return nil, err 442 } 443 if !crashed { 444 continue 445 } 446 res.Opts = opts 447 // Simplification successful, try extracting C repro. 448 res, err = ctx.extractC(res) 449 if err != nil { 450 return nil, err 451 } 452 if res.CRepro { 453 return res, nil 454 } 455 } 456 457 return res, nil 458} 459 460// Try triggering crash with a C reproducer. 461func (ctx *context) extractC(res *Result) (*Result, error) { 462 ctx.reproLog(2, "extracting C reproducer") 463 start := time.Now() 464 defer func() { 465 ctx.stats.ExtractCTime = time.Since(start) 466 }() 467 468 crashed, err := ctx.testCProg(res.Prog, res.Duration, res.Opts) 469 if err != nil { 470 return nil, err 471 } 472 res.CRepro = crashed 473 return res, nil 474} 475 476// Try to simplify the C reproducer. 477func (ctx *context) simplifyC(res *Result) (*Result, error) { 478 ctx.reproLog(2, "simplifying C reproducer") 479 start := time.Now() 480 defer func() { 481 ctx.stats.SimplifyCTime = time.Since(start) 482 }() 483 484 for _, simplify := range cSimplifies { 485 opts := res.Opts 486 if simplify(&opts) { 487 crashed, err := ctx.testCProg(res.Prog, res.Duration, opts) 488 if err != nil { 489 return nil, err 490 } 491 if crashed { 492 res.Opts = opts 493 } 494 } 495 } 496 return res, nil 497} 498 499func (ctx *context) testProg(p *prog.Prog, duration time.Duration, opts csource.Options) (crashed bool, err error) { 500 entry := prog.LogEntry{P: p} 501 if opts.Fault { 502 entry.Fault = true 503 entry.FaultCall = opts.FaultCall 504 entry.FaultNth = opts.FaultNth 505 } 506 return ctx.testProgs([]*prog.LogEntry{&entry}, duration, opts) 507} 508 509func (ctx *context) testProgs(entries []*prog.LogEntry, duration time.Duration, opts csource.Options) ( 510 crashed bool, err error) { 511 inst := <-ctx.instances 512 if inst == nil { 513 return false, fmt.Errorf("all VMs failed to boot") 514 } 515 defer ctx.returnInstance(inst) 516 if len(entries) == 0 { 517 return false, fmt.Errorf("no programs to execute") 518 } 519 520 pstr := encodeEntries(entries) 521 progFile, err := osutil.WriteTempFile(pstr) 522 if err != nil { 523 return false, err 524 } 525 defer os.Remove(progFile) 526 vmProgFile, err := inst.Copy(progFile) 527 if err != nil { 528 return false, fmt.Errorf("failed to copy to VM: %v", err) 529 } 530 531 if !opts.Fault { 532 opts.FaultCall = -1 533 } 534 program := entries[0].P.String() 535 if len(entries) > 1 { 536 program = "[" 537 for i, entry := range entries { 538 program += fmt.Sprintf("%v", len(entry.P.Calls)) 539 if i != len(entries)-1 { 540 program += ", " 541 } 542 } 543 program += "]" 544 } 545 546 command := instancePkg.ExecprogCmd(inst.execprogBin, inst.executorBin, 547 ctx.cfg.TargetOS, ctx.cfg.TargetArch, opts.Sandbox, opts.Repeat, 548 opts.Threaded, opts.Collide, opts.Procs, -1, -1, vmProgFile) 549 ctx.reproLog(2, "testing program (duration=%v, %+v): %s", duration, opts, program) 550 return ctx.testImpl(inst.Instance, command, duration) 551} 552 553func (ctx *context) testCProg(p *prog.Prog, duration time.Duration, opts csource.Options) (crashed bool, err error) { 554 src, err := csource.Write(p, opts) 555 if err != nil { 556 return false, err 557 } 558 bin, err := csource.Build(p.Target, src) 559 if err != nil { 560 return false, err 561 } 562 defer os.Remove(bin) 563 ctx.reproLog(2, "testing compiled C program (duration=%v, %+v): %s", duration, opts, p) 564 crashed, err = ctx.testBin(bin, duration) 565 if err != nil { 566 return false, err 567 } 568 return crashed, nil 569} 570 571func (ctx *context) testBin(bin string, duration time.Duration) (crashed bool, err error) { 572 inst := <-ctx.instances 573 if inst == nil { 574 return false, fmt.Errorf("all VMs failed to boot") 575 } 576 defer ctx.returnInstance(inst) 577 578 bin, err = inst.Copy(bin) 579 if err != nil { 580 return false, fmt.Errorf("failed to copy to VM: %v", err) 581 } 582 return ctx.testImpl(inst.Instance, bin, duration) 583} 584 585func (ctx *context) testImpl(inst *vm.Instance, command string, duration time.Duration) (crashed bool, err error) { 586 outc, errc, err := inst.Run(duration, nil, command) 587 if err != nil { 588 return false, fmt.Errorf("failed to run command in VM: %v", err) 589 } 590 rep := inst.MonitorExecution(outc, errc, ctx.reporter, true) 591 if rep == nil { 592 ctx.reproLog(2, "program did not crash") 593 return false, nil 594 } 595 if rep.Suppressed { 596 ctx.reproLog(2, "suppressed program crash: %v", rep.Title) 597 return false, nil 598 } 599 ctx.report = rep 600 ctx.reproLog(2, "program crashed: %v", rep.Title) 601 return true, nil 602} 603 604func (ctx *context) returnInstance(inst *instance) { 605 ctx.bootRequests <- inst.index 606 inst.Close() 607} 608 609func (ctx *context) reproLog(level int, format string, args ...interface{}) { 610 prefix := fmt.Sprintf("reproducing crash '%v': ", ctx.crashTitle) 611 log.Logf(level, prefix+format, args...) 612 ctx.stats.Log = append(ctx.stats.Log, []byte(fmt.Sprintf(format, args...)+"\n")...) 613} 614 615func (ctx *context) bisectProgs(progs []*prog.LogEntry, pred func([]*prog.LogEntry) (bool, error)) ( 616 []*prog.LogEntry, error) { 617 ctx.reproLog(3, "bisect: bisecting %d programs", len(progs)) 618 619 ctx.reproLog(3, "bisect: executing all %d programs", len(progs)) 620 crashed, err := pred(progs) 621 if err != nil { 622 return nil, err 623 } 624 if !crashed { 625 ctx.reproLog(3, "bisect: didn't crash") 626 return nil, nil 627 } 628 629 guilty := [][]*prog.LogEntry{progs} 630again: 631 ctx.reproLog(3, "bisect: guilty chunks: %v", chunksToStr(guilty)) 632 for i, chunk := range guilty { 633 if len(chunk) == 1 { 634 continue 635 } 636 637 guilty1 := guilty[:i] 638 guilty2 := guilty[i+1:] 639 ctx.reproLog(3, "bisect: guilty chunks split: %v, <%v>, %v", 640 chunksToStr(guilty1), len(chunk), chunksToStr(guilty2)) 641 642 chunk1 := chunk[0 : len(chunk)/2] 643 chunk2 := chunk[len(chunk)/2:] 644 ctx.reproLog(3, "bisect: chunk split: <%v> => <%v>, <%v>", 645 len(chunk), len(chunk1), len(chunk2)) 646 647 ctx.reproLog(3, "bisect: triggering crash without chunk #1") 648 progs = flatenChunks(guilty1, guilty2, chunk2) 649 crashed, err := pred(progs) 650 if err != nil { 651 return nil, err 652 } 653 654 if crashed { 655 guilty = nil 656 guilty = append(guilty, guilty1...) 657 guilty = append(guilty, chunk2) 658 guilty = append(guilty, guilty2...) 659 ctx.reproLog(3, "bisect: crashed, chunk #1 evicted") 660 goto again 661 } 662 663 ctx.reproLog(3, "bisect: triggering crash without chunk #2") 664 progs = flatenChunks(guilty1, guilty2, chunk1) 665 crashed, err = pred(progs) 666 if err != nil { 667 return nil, err 668 } 669 670 if crashed { 671 guilty = nil 672 guilty = append(guilty, guilty1...) 673 guilty = append(guilty, chunk1) 674 guilty = append(guilty, guilty2...) 675 ctx.reproLog(3, "bisect: crashed, chunk #2 evicted") 676 goto again 677 } 678 679 guilty = nil 680 guilty = append(guilty, guilty1...) 681 guilty = append(guilty, chunk1) 682 guilty = append(guilty, chunk2) 683 guilty = append(guilty, guilty2...) 684 685 ctx.reproLog(3, "bisect: not crashed, both chunks required") 686 687 goto again 688 } 689 690 progs = nil 691 for _, chunk := range guilty { 692 if len(chunk) != 1 { 693 return nil, fmt.Errorf("bad bisect result: %v", guilty) 694 } 695 progs = append(progs, chunk[0]) 696 } 697 698 ctx.reproLog(3, "bisect: success, %d programs left", len(progs)) 699 return progs, nil 700} 701 702func flatenChunks(guilty1, guilty2 [][]*prog.LogEntry, chunk []*prog.LogEntry) []*prog.LogEntry { 703 var progs []*prog.LogEntry 704 for _, c := range guilty1 { 705 progs = append(progs, c...) 706 } 707 progs = append(progs, chunk...) 708 for _, c := range guilty2 { 709 progs = append(progs, c...) 710 } 711 return progs 712} 713 714func chunksToStr(chunks [][]*prog.LogEntry) string { 715 log := "[" 716 for i, chunk := range chunks { 717 log += fmt.Sprintf("<%d>", len(chunk)) 718 if i != len(chunks)-1 { 719 log += ", " 720 } 721 } 722 log += "]" 723 return log 724} 725 726func reverseEntries(entries []*prog.LogEntry) []*prog.LogEntry { 727 last := len(entries) - 1 728 for i := 0; i < len(entries)/2; i++ { 729 entries[i], entries[last-i] = entries[last-i], entries[i] 730 } 731 return entries 732} 733 734func encodeEntries(entries []*prog.LogEntry) []byte { 735 buf := new(bytes.Buffer) 736 for _, ent := range entries { 737 opts := "" 738 if ent.Fault { 739 opts = fmt.Sprintf(" (fault-call:%v fault-nth:%v)", ent.FaultCall, ent.FaultNth) 740 } 741 fmt.Fprintf(buf, "executing program %v%v:\n%v", ent.Proc, opts, string(ent.P.Serialize())) 742 } 743 return buf.Bytes() 744} 745 746type Simplify func(opts *csource.Options) bool 747 748var progSimplifies = []Simplify{ 749 func(opts *csource.Options) bool { 750 if !opts.Fault { 751 return false 752 } 753 opts.Fault = false 754 opts.FaultCall = 0 755 opts.FaultNth = 0 756 return true 757 }, 758 func(opts *csource.Options) bool { 759 if !opts.Collide { 760 return false 761 } 762 opts.Collide = false 763 return true 764 }, 765 func(opts *csource.Options) bool { 766 if opts.Collide || !opts.Threaded { 767 return false 768 } 769 opts.Threaded = false 770 return true 771 }, 772 func(opts *csource.Options) bool { 773 if !opts.Repeat { 774 return false 775 } 776 opts.Repeat = false 777 opts.EnableCgroups = false 778 opts.ResetNet = false 779 opts.Procs = 1 780 return true 781 }, 782 func(opts *csource.Options) bool { 783 if opts.Procs == 1 { 784 return false 785 } 786 opts.Procs = 1 787 return true 788 }, 789 func(opts *csource.Options) bool { 790 if opts.Sandbox == "none" { 791 return false 792 } 793 opts.Sandbox = "none" 794 return true 795 }, 796} 797 798var cSimplifies = append(progSimplifies, []Simplify{ 799 func(opts *csource.Options) bool { 800 if opts.Sandbox == "" { 801 return false 802 } 803 opts.Sandbox = "" 804 opts.EnableTun = false 805 opts.EnableCgroups = false 806 opts.EnableNetdev = false 807 opts.ResetNet = false 808 return true 809 }, 810 func(opts *csource.Options) bool { 811 if !opts.EnableTun { 812 return false 813 } 814 opts.EnableTun = false 815 return true 816 }, 817 func(opts *csource.Options) bool { 818 if !opts.EnableCgroups { 819 return false 820 } 821 opts.EnableCgroups = false 822 return true 823 }, 824 func(opts *csource.Options) bool { 825 if !opts.EnableNetdev { 826 return false 827 } 828 opts.EnableNetdev = false 829 return true 830 }, 831 func(opts *csource.Options) bool { 832 if !opts.ResetNet { 833 return false 834 } 835 opts.ResetNet = false 836 return true 837 }, 838 func(opts *csource.Options) bool { 839 if !opts.UseTmpDir || opts.Sandbox == "namespace" || opts.EnableCgroups { 840 return false 841 } 842 opts.UseTmpDir = false 843 return true 844 }, 845 func(opts *csource.Options) bool { 846 if !opts.HandleSegv { 847 return false 848 } 849 opts.HandleSegv = false 850 return true 851 }, 852}...) 853