github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/repro/repro.go (about)

     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  
     4  package repro
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"sort"
    12  	"time"
    13  
    14  	"github.com/google/syzkaller/pkg/bisect/minimize"
    15  	"github.com/google/syzkaller/pkg/csource"
    16  	"github.com/google/syzkaller/pkg/flatrpc"
    17  	"github.com/google/syzkaller/pkg/instance"
    18  	"github.com/google/syzkaller/pkg/log"
    19  	"github.com/google/syzkaller/pkg/mgrconfig"
    20  	"github.com/google/syzkaller/pkg/report"
    21  	"github.com/google/syzkaller/pkg/report/crash"
    22  	"github.com/google/syzkaller/prog"
    23  	"github.com/google/syzkaller/sys/targets"
    24  	"github.com/google/syzkaller/vm"
    25  	"github.com/google/syzkaller/vm/dispatcher"
    26  )
    27  
    28  type Result struct {
    29  	Prog     *prog.Prog
    30  	Duration time.Duration
    31  	Opts     csource.Options
    32  	CRepro   bool
    33  	// Information about the final (non-symbolized) crash that we reproduced.
    34  	// Can be different from what we started reproducing.
    35  	Report *report.Report
    36  	// A very rough estimate of the probability with which the resulting syz
    37  	// reproducer crashes the kernel.
    38  	Reliability float64
    39  }
    40  
    41  type Stats struct {
    42  	Log              []byte
    43  	TotalTime        time.Duration
    44  	ExtractProgTime  time.Duration
    45  	MinimizeProgTime time.Duration
    46  	SimplifyProgTime time.Duration
    47  	ExtractCTime     time.Duration
    48  	SimplifyCTime    time.Duration
    49  }
    50  
    51  type reproContext struct {
    52  	ctx            context.Context
    53  	exec           execInterface
    54  	logf           func(string, ...interface{})
    55  	target         *targets.Target
    56  	crashTitle     string
    57  	crashType      crash.Type
    58  	crashStart     int
    59  	crashExecutor  *report.ExecutorInfo
    60  	entries        []*prog.LogEntry
    61  	testTimeouts   []time.Duration
    62  	startOpts      csource.Options
    63  	stats          *Stats
    64  	report         *report.Report
    65  	timeouts       targets.Timeouts
    66  	observedTitles map[string]bool
    67  	fast           bool
    68  }
    69  
    70  // execInterface describes the interfaces needed by pkg/repro.
    71  type execInterface interface {
    72  	// Run() will either run a C repro or a syz repro depending on params.
    73  	Run(ctx context.Context, params instance.ExecParams, logf instance.ExecutorLogger) (*instance.RunResult, error)
    74  }
    75  
    76  type Environment struct {
    77  	Config   *mgrconfig.Config
    78  	Features flatrpc.Feature
    79  	Reporter *report.Reporter
    80  	Pool     *vm.Dispatcher
    81  	// The Fast repro mode restricts the repro log bisection,
    82  	// it skips multiple simpifications and C repro generation.
    83  	Fast bool
    84  
    85  	logf func(string, ...interface{})
    86  }
    87  
    88  func Run(ctx context.Context, log []byte, env Environment) (*Result, *Stats, error) {
    89  	return runInner(ctx, log, env, &poolWrapper{
    90  		cfg:      env.Config,
    91  		reporter: env.Reporter,
    92  		pool:     env.Pool,
    93  	})
    94  }
    95  
    96  var ErrEmptyCrashLog = errors.New("no programs")
    97  
    98  func runInner(ctx context.Context, crashLog []byte, env Environment, exec execInterface) (*Result, *Stats, error) {
    99  	cfg := env.Config
   100  	entries := cfg.Target.ParseLog(crashLog, prog.NonStrict)
   101  	if len(entries) == 0 {
   102  		return nil, nil, fmt.Errorf("log (%d bytes) parse failed: %w", len(crashLog), ErrEmptyCrashLog)
   103  	}
   104  	crashStart := len(crashLog)
   105  	crashTitle, crashType := "", crash.UnknownType
   106  	var crashExecutor *report.ExecutorInfo
   107  	if rep := env.Reporter.Parse(crashLog); rep != nil {
   108  		crashStart = rep.StartPos
   109  		crashTitle = rep.Title
   110  		crashType = rep.Type
   111  		crashExecutor = rep.Executor
   112  	}
   113  	testTimeouts := []time.Duration{
   114  		max(30*time.Second, 3*cfg.Timeouts.Program), // to catch simpler crashes (i.e. no races and no hangs)
   115  		max(100*time.Second, 20*cfg.Timeouts.Program),
   116  		cfg.Timeouts.NoOutputRunningTime, // to catch "no output", races and hangs
   117  	}
   118  	switch {
   119  	case crashTitle == "":
   120  		crashTitle = "no output/lost connection"
   121  		// Lost connection can be detected faster,
   122  		// but theoretically if it's caused by a race it may need the largest timeout.
   123  		// No output can only be reproduced with the max timeout.
   124  		// As a compromise we use the smallest and the largest timeouts.
   125  		testTimeouts = []time.Duration{testTimeouts[0], testTimeouts[2]}
   126  	case crashType == crash.MemoryLeak:
   127  		// Memory leaks can't be detected quickly because of expensive setup and scanning.
   128  		testTimeouts = testTimeouts[1:]
   129  	case crashType == crash.Hang:
   130  		testTimeouts = testTimeouts[2:]
   131  	}
   132  	if env.Fast {
   133  		testTimeouts = []time.Duration{30 * time.Second, 5 * time.Minute}
   134  	}
   135  	reproCtx := &reproContext{
   136  		ctx:           ctx,
   137  		exec:          exec,
   138  		target:        cfg.SysTarget,
   139  		crashTitle:    crashTitle,
   140  		crashType:     crashType,
   141  		crashStart:    crashStart,
   142  		crashExecutor: crashExecutor,
   143  
   144  		entries:        entries,
   145  		testTimeouts:   testTimeouts,
   146  		startOpts:      createStartOptions(cfg, env.Features, crashType),
   147  		stats:          new(Stats),
   148  		timeouts:       cfg.Timeouts,
   149  		observedTitles: map[string]bool{},
   150  		fast:           env.Fast,
   151  		logf:           env.logf,
   152  	}
   153  	return reproCtx.run()
   154  }
   155  
   156  func (ctx *reproContext) run() (*Result, *Stats, error) {
   157  	res, err := ctx.repro()
   158  	if err != nil {
   159  		return nil, nil, err
   160  	}
   161  	if res != nil {
   162  		ctx.reproLogf(3, "repro crashed as (corrupted=%v):\n%s",
   163  			ctx.report.Corrupted, ctx.report.Report)
   164  		// Try to rerun the repro if the report is corrupted.
   165  		for attempts := 0; ctx.report.Corrupted && attempts < 3; attempts++ {
   166  			ctx.reproLogf(3, "report is corrupted, running repro again")
   167  			if res.CRepro {
   168  				_, err = ctx.testCProg(res.Prog, res.Duration, res.Opts, false)
   169  			} else {
   170  				_, err = ctx.testProg(res.Prog, res.Duration, res.Opts, false)
   171  			}
   172  			if err != nil {
   173  				return nil, nil, err
   174  			}
   175  		}
   176  		ctx.reproLogf(3, "final repro crashed as (corrupted=%v):\n%s",
   177  			ctx.report.Corrupted, ctx.report.Report)
   178  		res.Report = ctx.report
   179  	}
   180  	return res, ctx.stats, nil
   181  }
   182  
   183  func createStartOptions(cfg *mgrconfig.Config, features flatrpc.Feature,
   184  	crashType crash.Type) csource.Options {
   185  	opts := csource.DefaultOpts(cfg)
   186  	if crashType == crash.MemoryLeak {
   187  		opts.Leak = true
   188  	}
   189  	if features&flatrpc.FeatureNetInjection == 0 {
   190  		opts.NetInjection = false
   191  	}
   192  	if features&flatrpc.FeatureNetDevices == 0 {
   193  		opts.NetDevices = false
   194  	}
   195  	if features&flatrpc.FeatureDevlinkPCI == 0 {
   196  		opts.DevlinkPCI = false
   197  	}
   198  	if features&flatrpc.FeatureNicVF == 0 {
   199  		opts.NicVF = false
   200  	}
   201  	if features&flatrpc.FeatureUSBEmulation == 0 {
   202  		opts.USB = false
   203  	}
   204  	if features&flatrpc.FeatureVhciInjection == 0 {
   205  		opts.VhciInjection = false
   206  	}
   207  	if features&flatrpc.FeatureWifiEmulation == 0 {
   208  		opts.Wifi = false
   209  	}
   210  	if features&flatrpc.FeatureLRWPANEmulation == 0 {
   211  		opts.IEEE802154 = false
   212  	}
   213  	if features&flatrpc.FeatureSwap == 0 {
   214  		opts.Swap = false
   215  	}
   216  	return opts
   217  }
   218  
   219  func (ctx *reproContext) repro() (*Result, error) {
   220  	// Cut programs that were executed after crash.
   221  	for i, ent := range ctx.entries {
   222  		if ent.Start > ctx.crashStart {
   223  			ctx.entries = ctx.entries[:i]
   224  			break
   225  		}
   226  	}
   227  
   228  	reproStart := time.Now()
   229  	defer func() {
   230  		ctx.reproLogf(3, "reproducing took %s", time.Since(reproStart))
   231  		ctx.stats.TotalTime = time.Since(reproStart)
   232  	}()
   233  
   234  	res, err := ctx.extractProg(ctx.entries)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	if res == nil {
   239  		return nil, nil
   240  	}
   241  	res, err = ctx.minimizeProg(res)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	// Try extracting C repro without simplifying options first.
   247  	if !ctx.fast {
   248  		res, err = ctx.extractC(res)
   249  		if err != nil {
   250  			return nil, err
   251  		}
   252  
   253  		// Simplify options and try extracting C repro.
   254  		if !res.CRepro {
   255  			res, err = ctx.simplifyProg(res)
   256  			if err != nil {
   257  				return nil, err
   258  			}
   259  		}
   260  
   261  		// Simplify C related options.
   262  		if res.CRepro {
   263  			res, err = ctx.simplifyC(res)
   264  			if err != nil {
   265  				return nil, err
   266  			}
   267  		}
   268  	}
   269  	// Validate the resulting reproducer - a random rare kernel crash might have diverted the process.
   270  	res.Reliability, err = calculateReliability(func() (bool, error) {
   271  		ret, err := ctx.testProg(res.Prog, res.Duration, res.Opts, false)
   272  		if err != nil {
   273  			return false, err
   274  		}
   275  		ctx.reproLogf(2, "validation run: crashed=%v", ret.Crashed)
   276  		return ret.Crashed, nil
   277  	})
   278  	if err != nil {
   279  		ctx.reproLogf(2, "could not calculate reliability, err=%v", err)
   280  		return nil, err
   281  	}
   282  
   283  	const minReliability = 0.15
   284  	if res.Reliability < minReliability {
   285  		ctx.reproLogf(1, "reproducer is too unreliable: %.2f", res.Reliability)
   286  		return nil, err
   287  	}
   288  
   289  	return res, nil
   290  }
   291  
   292  func calculateReliability(cb func() (bool, error)) (float64, error) {
   293  	const (
   294  		maxRuns  = 10
   295  		enoughOK = 3
   296  	)
   297  	total := 0
   298  	okCount := 0
   299  	for i := 0; i < maxRuns && okCount < enoughOK; i++ {
   300  		total++
   301  		ok, err := cb()
   302  		if err != nil {
   303  			return 0, err
   304  		}
   305  		if ok {
   306  			okCount++
   307  		}
   308  	}
   309  	return float64(okCount) / float64(total), nil
   310  }
   311  
   312  func (ctx *reproContext) extractProg(entries []*prog.LogEntry) (*Result, error) {
   313  	ctx.reproLogf(2, "extracting reproducer from %v programs", len(entries))
   314  	start := time.Now()
   315  	defer func() {
   316  		ctx.stats.ExtractProgTime = time.Since(start)
   317  	}()
   318  
   319  	var toTest []*prog.LogEntry
   320  	if ctx.crashExecutor != nil {
   321  		for _, entry := range entries {
   322  			// Note: we don't check ProcID b/c hanged programs are assigned fake unique proc IDs
   323  			// that don't match "Comm" in the kernel panic message.
   324  			if entry.ID == ctx.crashExecutor.ExecID {
   325  				toTest = append(toTest, entry)
   326  				ctx.reproLogf(3, "first checking the prog from the crash report")
   327  				break
   328  			}
   329  		}
   330  	}
   331  
   332  	if len(toTest) == 0 {
   333  		ctx.reproLogf(3, "testing a last program of every proc")
   334  		toTest = lastEntries(entries)
   335  	}
   336  
   337  	for i, timeout := range ctx.testTimeouts {
   338  		// Execute each program separately to detect simple crashes caused by a single program.
   339  		// Programs are executed in reverse order, usually the last program is the guilty one.
   340  		res, err := ctx.extractProgSingle(toTest, timeout)
   341  		if err != nil {
   342  			return nil, err
   343  		}
   344  		if res != nil {
   345  			ctx.reproLogf(3, "found reproducer with %d syscalls", len(res.Prog.Calls))
   346  			return res, nil
   347  		}
   348  
   349  		// Don't try bisecting if there's only one entry.
   350  		if len(entries) == 1 {
   351  			continue
   352  		}
   353  
   354  		if ctx.fast && i+1 < len(ctx.testTimeouts) {
   355  			// Bisect only under the biggest timeout.
   356  			continue
   357  		}
   358  
   359  		// Execute all programs and bisect the log to find multiple guilty programs.
   360  		res, err = ctx.extractProgBisect(entries, timeout)
   361  		if err != nil {
   362  			return nil, err
   363  		}
   364  		if res != nil {
   365  			ctx.reproLogf(3, "found reproducer with %d syscalls", len(res.Prog.Calls))
   366  			return res, nil
   367  		}
   368  	}
   369  
   370  	ctx.reproLogf(2, "failed to extract reproducer")
   371  	return nil, nil
   372  }
   373  
   374  // Extract last program on every proc.
   375  func lastEntries(entries []*prog.LogEntry) []*prog.LogEntry {
   376  	procs := make(map[int]int)
   377  	for i, ent := range entries {
   378  		procs[ent.Proc] = i
   379  	}
   380  	var indices []int
   381  	for _, idx := range procs {
   382  		indices = append(indices, idx)
   383  	}
   384  	sort.Ints(indices)
   385  	var lastEntries []*prog.LogEntry
   386  	for i := len(indices) - 1; i >= 0; i-- {
   387  		lastEntries = append(lastEntries, entries[indices[i]])
   388  	}
   389  	return lastEntries
   390  }
   391  
   392  func (ctx *reproContext) extractProgSingle(entries []*prog.LogEntry, duration time.Duration) (*Result, error) {
   393  	ctx.reproLogf(3, "single: executing %d programs separately with timeout %s", len(entries), duration)
   394  
   395  	opts := ctx.startOpts
   396  	for _, ent := range entries {
   397  		ret, err := ctx.testProg(ent.P, duration, opts, false)
   398  		if err != nil {
   399  			return nil, err
   400  		}
   401  		if ret.Crashed {
   402  			res := &Result{
   403  				Prog:     ent.P,
   404  				Duration: max(duration, ret.Duration*3/2),
   405  				Opts:     opts,
   406  			}
   407  			ctx.reproLogf(3, "single: successfully extracted reproducer")
   408  			return res, nil
   409  		}
   410  	}
   411  
   412  	ctx.reproLogf(3, "single: failed to extract reproducer")
   413  	return nil, nil
   414  }
   415  
   416  func (ctx *reproContext) extractProgBisect(entries []*prog.LogEntry, baseDuration time.Duration) (*Result, error) {
   417  	ctx.reproLogf(3, "bisect: bisecting %d programs with base timeout %s", len(entries), baseDuration)
   418  
   419  	opts := ctx.startOpts
   420  	duration := func(entries int) time.Duration {
   421  		return baseDuration + time.Duration(entries/4)*time.Second
   422  	}
   423  
   424  	// First check if replaying the log may crash the kernel at all.
   425  	ret, err := ctx.testProgs(entries, duration(len(entries)), opts, false)
   426  	if !ret.Crashed {
   427  		ctx.reproLogf(3, "replaying the whole log did not cause a kernel crash")
   428  		return nil, nil
   429  	}
   430  	if err != nil {
   431  		return nil, err
   432  	}
   433  
   434  	// Bisect the log to find multiple guilty programs.
   435  	entries, err = ctx.bisectProgs(entries, func(progs []*prog.LogEntry) (bool, error) {
   436  		ret, err := ctx.testProgs(progs, duration(len(progs)), opts, false)
   437  		return ret.Crashed, err
   438  	})
   439  	if err != nil {
   440  		return nil, err
   441  	}
   442  	if len(entries) == 0 {
   443  		return nil, nil
   444  	}
   445  
   446  	// TODO: Minimize each program before concatenation.
   447  	// TODO: Return multiple programs if concatenation fails.
   448  
   449  	ctx.reproLogf(3, "bisect: %d programs left: \n\n%s\n", len(entries), encodeEntries(entries))
   450  	ctx.reproLogf(3, "bisect: trying to concatenate")
   451  
   452  	// Concatenate all programs into one.
   453  	dur := duration(len(entries)) * 3 / 2
   454  	return ctx.concatenateProgs(entries, dur)
   455  }
   456  
   457  // The bisected progs may exceed the prog.MaxCalls limit.
   458  // So let's first try to drop unneeded calls.
   459  func (ctx *reproContext) concatenateProgs(entries []*prog.LogEntry, dur time.Duration) (*Result, error) {
   460  	ctx.reproLogf(3, "bisect: concatenate %d entries", len(entries))
   461  	if len(entries) > 1 {
   462  		// There's a risk of exceeding prog.MaxCalls, so let's first minimize
   463  		// all entries separately.
   464  		for i := 0; i < len(entries); i++ {
   465  			var testErr error
   466  			ctx.reproLogf(2, "minimizing program #%d before concatenation", i)
   467  			callsBefore := len(entries[i].P.Calls)
   468  			entries[i].P, _ = prog.Minimize(entries[i].P, -1, prog.MinimizeCallsOnly,
   469  				func(p1 *prog.Prog, _ int) bool {
   470  					if testErr != nil {
   471  						return false
   472  					}
   473  					var newEntries []*prog.LogEntry
   474  					if i > 0 {
   475  						newEntries = append(newEntries, entries[:i]...)
   476  					}
   477  					newEntries = append(newEntries, &prog.LogEntry{
   478  						P: p1,
   479  					})
   480  					if i+1 < len(entries) {
   481  						newEntries = append(newEntries, entries[i+1:]...)
   482  					}
   483  					ret, err := ctx.testProgs(newEntries, dur, ctx.startOpts, false)
   484  					if err != nil {
   485  						testErr = err
   486  						ctx.reproLogf(0, "concatenation step failed with %v", err)
   487  						return false
   488  					}
   489  					return ret.Crashed
   490  				})
   491  			if testErr != nil {
   492  				return nil, testErr
   493  			}
   494  			ctx.reproLogf(2, "minimized %d calls -> %d calls", callsBefore, len(entries[i].P.Calls))
   495  		}
   496  	}
   497  	p := &prog.Prog{
   498  		Target: entries[0].P.Target,
   499  	}
   500  	for _, entry := range entries {
   501  		p.Calls = append(p.Calls, entry.P.Calls...)
   502  	}
   503  	if len(p.Calls) > prog.MaxCalls {
   504  		ctx.reproLogf(2, "bisect: concatenated prog still exceeds %d calls", prog.MaxCalls)
   505  		return nil, nil
   506  	}
   507  	ret, err := ctx.testProg(p, dur, ctx.startOpts, false)
   508  	if err != nil {
   509  		ctx.reproLogf(3, "bisect: error during concatenation testing: %v", err)
   510  		return nil, err
   511  	}
   512  	if !ret.Crashed {
   513  		ctx.reproLogf(3, "bisect: concatenated prog does not crash")
   514  		return nil, nil
   515  	}
   516  	res := &Result{
   517  		Prog:     p,
   518  		Duration: min(dur, ret.Duration*2),
   519  		Opts:     ctx.startOpts,
   520  	}
   521  	ctx.reproLogf(3, "bisect: concatenation succeeded")
   522  	return res, nil
   523  }
   524  
   525  // Minimize calls and arguments.
   526  func (ctx *reproContext) minimizeProg(res *Result) (*Result, error) {
   527  	ctx.reproLogf(2, "minimizing guilty program")
   528  	start := time.Now()
   529  	defer func() {
   530  		ctx.stats.MinimizeProgTime = time.Since(start)
   531  	}()
   532  
   533  	mode := prog.MinimizeCrash
   534  	if ctx.fast {
   535  		mode = prog.MinimizeCallsOnly
   536  	}
   537  	var testErr error
   538  	res.Prog, _ = prog.Minimize(res.Prog, -1, mode, func(p1 *prog.Prog, callIndex int) bool {
   539  		if testErr != nil {
   540  			return false
   541  		}
   542  		if len(p1.Calls) == 0 {
   543  			// We do want to keep at least one call, otherwise tools/syz-execprog
   544  			// will immediately exit.
   545  			return false
   546  		}
   547  		ret, err := ctx.testProg(p1, res.Duration, res.Opts, false)
   548  		if err != nil {
   549  			ctx.reproLogf(2, "minimization failed with %v", err)
   550  			testErr = err
   551  			return false
   552  		}
   553  		return ret.Crashed
   554  	})
   555  	if testErr != nil {
   556  		return res, nil
   557  	}
   558  	return res, nil
   559  }
   560  
   561  // Simplify repro options (threaded, sandbox, etc).
   562  func (ctx *reproContext) simplifyProg(res *Result) (*Result, error) {
   563  	ctx.reproLogf(2, "simplifying guilty program options")
   564  	start := time.Now()
   565  	defer func() {
   566  		ctx.stats.SimplifyProgTime = time.Since(start)
   567  	}()
   568  
   569  	// Do further simplifications.
   570  	for _, simplify := range progSimplifies {
   571  		opts := res.Opts
   572  		if !simplify(&opts) || !checkOpts(&opts, ctx.timeouts, res.Duration) {
   573  			continue
   574  		}
   575  		ret, err := ctx.testProg(res.Prog, res.Duration, opts, true)
   576  		if err != nil {
   577  			return nil, err
   578  		}
   579  		if !ret.Crashed {
   580  			continue
   581  		}
   582  		res.Opts = opts
   583  		if ctx.fast {
   584  			continue
   585  		}
   586  		// Simplification successful, try extracting C repro.
   587  		res, err = ctx.extractC(res)
   588  		if err != nil {
   589  			return nil, err
   590  		}
   591  		if res.CRepro {
   592  			return res, nil
   593  		}
   594  	}
   595  
   596  	return res, nil
   597  }
   598  
   599  // Try triggering crash with a C reproducer.
   600  func (ctx *reproContext) extractC(res *Result) (*Result, error) {
   601  	ctx.reproLogf(2, "extracting C reproducer")
   602  	start := time.Now()
   603  	defer func() {
   604  		ctx.stats.ExtractCTime = time.Since(start)
   605  	}()
   606  
   607  	ret, err := ctx.testCProg(res.Prog, res.Duration, res.Opts, true)
   608  	if err != nil {
   609  		return nil, err
   610  	}
   611  	res.CRepro = ret.Crashed
   612  	return res, nil
   613  }
   614  
   615  // Try to simplify the C reproducer.
   616  func (ctx *reproContext) simplifyC(res *Result) (*Result, error) {
   617  	ctx.reproLogf(2, "simplifying C reproducer")
   618  	start := time.Now()
   619  	defer func() {
   620  		ctx.stats.SimplifyCTime = time.Since(start)
   621  	}()
   622  
   623  	for _, simplify := range cSimplifies {
   624  		opts := res.Opts
   625  		if !simplify(&opts) || !checkOpts(&opts, ctx.timeouts, res.Duration) {
   626  			continue
   627  		}
   628  		ret, err := ctx.testCProg(res.Prog, res.Duration, opts, true)
   629  		if err != nil {
   630  			return nil, err
   631  		}
   632  		if !ret.Crashed {
   633  			continue
   634  		}
   635  		res.Opts = opts
   636  	}
   637  	return res, nil
   638  }
   639  
   640  func checkOpts(opts *csource.Options, timeouts targets.Timeouts, timeout time.Duration) bool {
   641  	if !opts.Repeat && timeout >= time.Minute {
   642  		// If we have a non-repeating C reproducer with timeout > vm.NoOutputTimeout and it hangs
   643  		// (the reproducer itself does not terminate on its own, note: it does not have builtin timeout),
   644  		// then we will falsely detect "not output from test machine" kernel bug.
   645  		// We could fix it by adding a builtin timeout to such reproducers (like we have in all other cases).
   646  		// However, then it will exit within few seconds and we will finish the test without actually waiting
   647  		// for full vm.NoOutputTimeout, which breaks the whole reason of using vm.NoOutputTimeout in the first
   648  		// place. So we would need something more elaborate: let the program exist after few seconds, but
   649  		// continue waiting for kernel hang errors for minutes, but at the same time somehow ignore "no output"
   650  		// error because it will be false in this case.
   651  		// Instead we simply prohibit !Repeat with long timeouts.
   652  		// It makes sense on its own to some degree: if we are chasing an elusive bug, repeating the test
   653  		// will increase chances of reproducing it and can make the reproducer less flaky.
   654  		// Syz repros does not have this problem because they always have internal timeout, however
   655  		// (1) it makes sense on its own, (2) we will either not use the whole timeout or waste the remaining
   656  		// time as mentioned above, (3) if we remove repeat for syz repro, we won't be able to handle it
   657  		// when/if we switch to C repro (we can simplify options, but we can't "complicate" them back).
   658  		return false
   659  	}
   660  	return true
   661  }
   662  
   663  func (ctx *reproContext) testProg(p *prog.Prog, duration time.Duration, opts csource.Options,
   664  	strict bool) (ret verdict, err error) {
   665  	entry := prog.LogEntry{P: p}
   666  	return ctx.testProgs([]*prog.LogEntry{&entry}, duration, opts, strict)
   667  }
   668  
   669  type verdict struct {
   670  	Crashed  bool
   671  	Duration time.Duration
   672  }
   673  
   674  func (ctx *reproContext) getVerdict(callback func() (rep *instance.RunResult, err error), strict bool) (
   675  	verdict, error) {
   676  	var result *instance.RunResult
   677  	var err error
   678  
   679  	const attempts = 3
   680  	for i := 0; i < attempts; i++ {
   681  		// It's hard to classify all kinds of errors into the one worth repeating
   682  		// and not. So let's just retry runs for all errors.
   683  		// If the problem is transient, it will likely go away.
   684  		// If the problem is permanent, it will just be the same.
   685  		result, err = callback()
   686  		if err == nil {
   687  			break
   688  		}
   689  	}
   690  	if err != nil {
   691  		return verdict{}, err
   692  	}
   693  	rep := result.Report
   694  	if rep == nil {
   695  		return verdict{false, result.Duration}, nil
   696  	}
   697  	if rep.Suppressed {
   698  		ctx.reproLogf(2, "suppressed program crash: %v", rep.Title)
   699  		return verdict{false, result.Duration}, nil
   700  	}
   701  	if ctx.crashType == crash.MemoryLeak && rep.Type != crash.MemoryLeak {
   702  		ctx.reproLogf(2, "not a leak crash: %v", rep.Title)
   703  		return verdict{false, result.Duration}, nil
   704  	}
   705  	if strict && len(ctx.observedTitles) > 0 {
   706  		if !ctx.observedTitles[rep.Title] {
   707  			ctx.reproLogf(2, "a never seen crash title: %v, ignore", rep.Title)
   708  			return verdict{false, result.Duration}, nil
   709  		}
   710  	} else {
   711  		ctx.observedTitles[rep.Title] = true
   712  	}
   713  	ctx.report = rep
   714  	return verdict{true, result.Duration}, nil
   715  }
   716  
   717  var ErrNoVMs = errors.New("all VMs failed to boot")
   718  
   719  func encodeEntries(entries []*prog.LogEntry) []byte {
   720  	buf := new(bytes.Buffer)
   721  	for _, ent := range entries {
   722  		if len(ent.P.Calls) > prog.MaxCalls {
   723  			panic("prog.MaxCalls is exceeded")
   724  		}
   725  		fmt.Fprintf(buf, "executing program %v:\n%v", ent.Proc, string(ent.P.Serialize()))
   726  	}
   727  	return buf.Bytes()
   728  }
   729  
   730  func (ctx *reproContext) testProgs(entries []*prog.LogEntry, duration time.Duration, opts csource.Options,
   731  	strict bool) (ret verdict, err error) {
   732  	if len(entries) == 0 {
   733  		return ret, fmt.Errorf("no programs to execute")
   734  	}
   735  	pstr := encodeEntries(entries)
   736  	program := entries[0].P.String()
   737  	if len(entries) > 1 {
   738  		program = "["
   739  		for i, entry := range entries {
   740  			program += fmt.Sprintf("%v", len(entry.P.Calls))
   741  			if i != len(entries)-1 {
   742  				program += ", "
   743  			}
   744  		}
   745  		program += "]"
   746  	}
   747  	ctx.reproLogf(2, "testing program (duration=%v, %+v): %s", duration, opts, program)
   748  	ctx.reproLogf(3, "detailed listing:\n%s", pstr)
   749  	return ctx.getVerdict(func() (*instance.RunResult, error) {
   750  		return ctx.exec.Run(ctx.ctx, instance.ExecParams{
   751  			SyzProg:  pstr,
   752  			Opts:     opts,
   753  			Duration: duration,
   754  		}, ctx.reproLogf)
   755  	}, strict)
   756  }
   757  
   758  func (ctx *reproContext) testCProg(p *prog.Prog, duration time.Duration, opts csource.Options,
   759  	strict bool) (ret verdict, err error) {
   760  	return ctx.getVerdict(func() (*instance.RunResult, error) {
   761  		return ctx.exec.Run(ctx.ctx, instance.ExecParams{
   762  			CProg:    p,
   763  			Opts:     opts,
   764  			Duration: duration,
   765  		}, ctx.reproLogf)
   766  	}, strict)
   767  }
   768  
   769  func (ctx *reproContext) reproLogf(level int, format string, args ...interface{}) {
   770  	if ctx.logf != nil {
   771  		ctx.logf(format, args...)
   772  	}
   773  	prefix := fmt.Sprintf("reproducing crash '%v': ", ctx.crashTitle)
   774  	log.Logf(level, prefix+format, args...)
   775  	ctx.stats.Log = append(ctx.stats.Log, []byte(fmt.Sprintf(format, args...)+"\n")...)
   776  }
   777  
   778  func (ctx *reproContext) bisectProgs(progs []*prog.LogEntry, pred func([]*prog.LogEntry) (bool, error)) (
   779  	[]*prog.LogEntry, error) {
   780  	// Set up progs bisection.
   781  	ctx.reproLogf(3, "bisect: bisecting %d programs", len(progs))
   782  	minimizePred := func(progs []*prog.LogEntry) (bool, error) {
   783  		// Don't waste time testing empty crash log.
   784  		if len(progs) == 0 {
   785  			return false, nil
   786  		}
   787  		return pred(progs)
   788  	}
   789  	// For flaky crashes we usually end up with too many chunks.
   790  	// Continuing bisection would just take a lot of time and likely produce no result.
   791  	chunks := 6
   792  	if ctx.fast {
   793  		chunks = 2
   794  	}
   795  	ret, err := minimize.SliceWithFixed(minimize.Config[*prog.LogEntry]{
   796  		Pred:      minimizePred,
   797  		MaxChunks: chunks,
   798  		Logf: func(msg string, args ...interface{}) {
   799  			ctx.reproLogf(3, "bisect: "+msg, args...)
   800  		},
   801  	}, progs, func(elem *prog.LogEntry) bool {
   802  		if ctx.crashExecutor == nil {
   803  			return false
   804  		}
   805  		// If the program was mentioned in the crash report, always keep it during bisection.
   806  		return elem.ID == ctx.crashExecutor.ExecID
   807  	})
   808  	if err == minimize.ErrTooManyChunks {
   809  		ctx.reproLogf(3, "bisect: too many guilty chunks, aborting")
   810  		return nil, nil
   811  	}
   812  	return ret, err
   813  }
   814  
   815  type poolWrapper struct {
   816  	cfg      *mgrconfig.Config
   817  	reporter *report.Reporter
   818  	pool     *vm.Dispatcher
   819  }
   820  
   821  func (pw *poolWrapper) Run(ctx context.Context, params instance.ExecParams,
   822  	logf instance.ExecutorLogger) (*instance.RunResult, error) {
   823  	if err := ctx.Err(); err != nil {
   824  		// Note that we could also propagate ctx down to SetupExecProg() and RunCProg() operations,
   825  		// but so far it does not seem to be worth the effort.
   826  		return nil, err
   827  	}
   828  
   829  	var result *instance.RunResult
   830  	var err error
   831  	runErr := pw.pool.Run(ctx, func(ctx context.Context, inst *vm.Instance, updInfo dispatcher.UpdateInfo) {
   832  		updInfo(func(info *dispatcher.Info) {
   833  			typ := "syz"
   834  			if params.CProg != nil {
   835  				typ = "C"
   836  			}
   837  			info.Status = fmt.Sprintf("reproducing (%s, %.1f min)", typ, params.Duration.Minutes())
   838  		})
   839  		var ret *instance.ExecProgInstance
   840  		ret, err = instance.SetupExecProg(inst, pw.cfg, pw.reporter,
   841  			&instance.OptionalConfig{Logf: logf})
   842  		if err != nil {
   843  			return
   844  		}
   845  		if params.CProg != nil {
   846  			result, err = ret.RunCProg(params)
   847  		} else {
   848  			result, err = ret.RunSyzProg(params)
   849  		}
   850  	})
   851  	if runErr != nil {
   852  		return nil, runErr
   853  	}
   854  	return result, err
   855  }
   856  
   857  type Simplify func(opts *csource.Options) bool
   858  
   859  var progSimplifies = []Simplify{
   860  	func(opts *csource.Options) bool {
   861  		if opts.Collide || !opts.Threaded {
   862  			return false
   863  		}
   864  		opts.Threaded = false
   865  		return true
   866  	},
   867  	func(opts *csource.Options) bool {
   868  		if !opts.Repeat {
   869  			return false
   870  		}
   871  		opts.Repeat = false
   872  		opts.Cgroups = false
   873  		opts.NetReset = false
   874  		opts.Procs = 1
   875  		return true
   876  	},
   877  	func(opts *csource.Options) bool {
   878  		if opts.Procs == 1 {
   879  			return false
   880  		}
   881  		opts.Procs = 1
   882  		return true
   883  	},
   884  	func(opts *csource.Options) bool {
   885  		if opts.Sandbox == "none" {
   886  			return false
   887  		}
   888  		opts.Sandbox = "none"
   889  		return true
   890  	},
   891  }
   892  
   893  var cSimplifies = append(progSimplifies, []Simplify{
   894  	func(opts *csource.Options) bool {
   895  		if opts.Sandbox == "" {
   896  			return false
   897  		}
   898  		opts.Sandbox = ""
   899  		opts.NetInjection = false
   900  		opts.NetDevices = false
   901  		opts.NetReset = false
   902  		opts.Cgroups = false
   903  		opts.BinfmtMisc = false
   904  		opts.CloseFDs = false
   905  		opts.DevlinkPCI = false
   906  		opts.NicVF = false
   907  		opts.USB = false
   908  		opts.VhciInjection = false
   909  		opts.Wifi = false
   910  		opts.Swap = false
   911  		return true
   912  	},
   913  	func(opts *csource.Options) bool {
   914  		if !opts.NetInjection {
   915  			return false
   916  		}
   917  		opts.NetInjection = false
   918  		return true
   919  	},
   920  	func(opts *csource.Options) bool {
   921  		if !opts.NetDevices {
   922  			return false
   923  		}
   924  		opts.NetDevices = false
   925  		return true
   926  	},
   927  	func(opts *csource.Options) bool {
   928  		if !opts.NetReset {
   929  			return false
   930  		}
   931  		opts.NetReset = false
   932  		return true
   933  	},
   934  	func(opts *csource.Options) bool {
   935  		if !opts.Cgroups {
   936  			return false
   937  		}
   938  		opts.Cgroups = false
   939  		return true
   940  	},
   941  	func(opts *csource.Options) bool {
   942  		if !opts.BinfmtMisc {
   943  			return false
   944  		}
   945  		opts.BinfmtMisc = false
   946  		return true
   947  	},
   948  	func(opts *csource.Options) bool {
   949  		// We don't want to remove close_fds() call when repeat is enabled,
   950  		// since that can lead to deadlocks, see executor/common_linux.h.
   951  		if !opts.CloseFDs || opts.Repeat {
   952  			return false
   953  		}
   954  		opts.CloseFDs = false
   955  		return true
   956  	},
   957  	func(opts *csource.Options) bool {
   958  		if !opts.DevlinkPCI {
   959  			return false
   960  		}
   961  		opts.DevlinkPCI = false
   962  		return true
   963  	},
   964  	func(opts *csource.Options) bool {
   965  		if !opts.NicVF {
   966  			return false
   967  		}
   968  		opts.NicVF = false
   969  		return true
   970  	},
   971  	func(opts *csource.Options) bool {
   972  		if !opts.USB {
   973  			return false
   974  		}
   975  		opts.USB = false
   976  		return true
   977  	},
   978  	func(opts *csource.Options) bool {
   979  		if !opts.VhciInjection {
   980  			return false
   981  		}
   982  		opts.VhciInjection = false
   983  		return true
   984  	},
   985  	func(opts *csource.Options) bool {
   986  		if !opts.Wifi {
   987  			return false
   988  		}
   989  		opts.Wifi = false
   990  		return true
   991  	},
   992  	func(opts *csource.Options) bool {
   993  		if !opts.IEEE802154 {
   994  			return false
   995  		}
   996  		opts.IEEE802154 = false
   997  		return true
   998  	},
   999  	func(opts *csource.Options) bool {
  1000  		if !opts.UseTmpDir || opts.Sandbox == "namespace" || opts.Cgroups {
  1001  			return false
  1002  		}
  1003  		opts.UseTmpDir = false
  1004  		return true
  1005  	},
  1006  	func(opts *csource.Options) bool {
  1007  		if !opts.HandleSegv {
  1008  			return false
  1009  		}
  1010  		opts.HandleSegv = false
  1011  		return true
  1012  	},
  1013  	func(opts *csource.Options) bool {
  1014  		if !opts.Sysctl {
  1015  			return false
  1016  		}
  1017  		opts.Sysctl = false
  1018  		return true
  1019  	},
  1020  	func(opts *csource.Options) bool {
  1021  		if !opts.Swap {
  1022  			return false
  1023  		}
  1024  		opts.Swap = false
  1025  		return true
  1026  	},
  1027  }...)
  1028  
  1029  func (stats *Stats) FullLog() []byte {
  1030  	if stats == nil {
  1031  		return nil
  1032  	}
  1033  	return []byte(fmt.Sprintf("Extracting prog: %v\nMinimizing prog: %v\n"+
  1034  		"Simplifying prog options: %v\nExtracting C: %v\nSimplifying C: %v\n\n\n%s",
  1035  		stats.ExtractProgTime, stats.MinimizeProgTime,
  1036  		stats.SimplifyProgTime, stats.ExtractCTime, stats.SimplifyCTime, stats.Log))
  1037  }
  1038  
  1039  func (repro *Result) CProgram() ([]byte, error) {
  1040  	cprog, err := csource.Write(repro.Prog, repro.Opts)
  1041  	if err == nil {
  1042  		formatted, err := csource.Format(cprog)
  1043  		if err == nil {
  1044  			return formatted, nil
  1045  		}
  1046  		return cprog, nil
  1047  	}
  1048  	return nil, err
  1049  }