diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2025-07-25 15:32:54 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2025-07-29 09:54:26 +0000 |
| commit | d052a4c8ea018bce39f2ffeed6ce214d78c4381b (patch) | |
| tree | 23b63f2120fcc85ee784fa329d619a1d4d6ebb00 | |
| parent | b14118bbe9ad644f8e16279255fed61d399b96fa (diff) | |
pkg/manager: do a full reproduction for patched-only bugs
After ensuring that a bug only affects the patched kernel, do one more
round of reproduction and (if successful) re-report the result.
This will ensure that, provided enough time, diff fuzzing results will
also have minimalistic C reproducers.
| -rw-r--r-- | pkg/manager/diff.go | 23 | ||||
| -rw-r--r-- | pkg/manager/repro.go | 15 |
2 files changed, 31 insertions, 7 deletions
diff --git a/pkg/manager/diff.go b/pkg/manager/diff.go index a072edfb8..a81a2995f 100644 --- a/pkg/manager/diff.go +++ b/pkg/manager/diff.go @@ -206,6 +206,17 @@ loop: }: } log.Logf(0, "patched-only: %s", ret.origReport.Title) + // Now that we know this bug only affects the patch kernel, we can spend more time + // generating a minimalistic repro and a C repro. + if !ret.fullRepro { + reproLoop.Enqueue(&Crash{ + Report: &report.Report{ + Title: ret.origReport.Title, + Output: ret.repro.Prog.Serialize(), + }, + FullRepro: true, + }) + } } else { dc.store.BaseCrashed(ret.origReport.Title, ret.origReport.Report) log.Logf(0, "crashes both: %s / %s", ret.origReport.Title, ret.crashReport.Title) @@ -220,7 +231,7 @@ loop: log.Logf(1, "found repro for %q (orig title: %q, reliability: %2.f), took %.2f minutes", ret.Repro.Report.Title, origTitle, ret.Repro.Reliability, ret.Stats.TotalTime.Minutes()) g.Go(func() error { - runner.Run(ctx, ret.Repro) + runner.Run(ctx, ret.Repro, ret.Crash.FullRepro) return nil }) } else { @@ -313,6 +324,9 @@ func (dc *diffContext) monitorPatchedCoverage(ctx context.Context) error { const maxReproAttempts = 6 func (dc *diffContext) NeedRepro(crash *Crash) bool { + if crash.FullRepro { + return true + } if strings.Contains(crash.Title, "no output") || strings.Contains(crash.Title, "lost connection") || strings.Contains(crash.Title, "stall") || @@ -341,7 +355,7 @@ func (dc *diffContext) RunRepro(ctx context.Context, crash *Crash) *ReproResult Features: dc.new.features, Reporter: dc.new.reporter, Pool: dc.new.pool, - Fast: true, + Fast: !crash.FullRepro, }) if res != nil && res.Report != nil { dc.mu.Lock() @@ -642,6 +656,7 @@ type reproRunnerResult struct { origReport *report.Report crashReport *report.Report repro *repro.Result + fullRepro bool // whether this was a full reproduction } const ( @@ -658,7 +673,7 @@ const ( // To avoid reporting false positives, the function does not require the kernel to crash with exactly // the same crash title as in the original crash report. Any single crash is accepted. // The result is sent back over the rr.done channel. -func (rr *reproRunner) Run(ctx context.Context, r *repro.Result) { +func (rr *reproRunner) Run(ctx context.Context, r *repro.Result, fullRepro bool) { if r.Reliability < reliabilityCutOff { log.Logf(1, "%s: repro is too unreliable, skipping", r.Report.Title) return @@ -676,7 +691,7 @@ func (rr *reproRunner) Run(ctx context.Context, r *repro.Result) { rr.kernel.pool.ReserveForRun(min(cnt, pool.Total())) }() - ret := reproRunnerResult{origReport: r.Report, repro: r} + ret := reproRunnerResult{origReport: r.Report, repro: r, fullRepro: fullRepro} for doneRuns := 0; doneRuns < needRuns; { if ctx.Err() != nil { return diff --git a/pkg/manager/repro.go b/pkg/manager/repro.go index 28b676752..e7034b186 100644 --- a/pkg/manager/repro.go +++ b/pkg/manager/repro.go @@ -29,18 +29,23 @@ type Crash struct { FromHub bool // this crash was created based on a repro from syz-hub FromDashboard bool // .. or from dashboard Manual bool + FullRepro bool // used by the diff fuzzer to do a full scale reproduction *report.Report } func (c *Crash) FullTitle() string { + suffix := "" + if c.FullRepro { + suffix = " (full)" + } if c.Report.Title != "" { - return c.Report.Title + return c.Report.Title + suffix } // Just use some unique, but stable titles. if c.FromDashboard { - return fmt.Sprintf("dashboard crash %p", c) + return fmt.Sprintf("dashboard crash %p%s", c, suffix) } else if c.FromHub { - return fmt.Sprintf("crash from hub %p", c) + return fmt.Sprintf("crash from hub %p%s", c, suffix) } panic("the crash is expected to have a report") } @@ -139,6 +144,10 @@ func (r *ReproLoop) popCrash() *Crash { defer r.mu.Unlock() newBetter := func(base, new *Crash) bool { + // If diff fuzzed has requested a full reproduction, do it first. + if base.FullRepro != new.FullRepro { + return new.FullRepro + } // The more times we failed, the less likely we are to actually // find a reproducer. Give preference to not yet attempted repro runs. baseTitle, newTitle := base.FullTitle(), new.FullTitle() |
