• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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