aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-07-25 15:32:54 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-07-29 09:54:26 +0000
commitd052a4c8ea018bce39f2ffeed6ce214d78c4381b (patch)
tree23b63f2120fcc85ee784fa329d619a1d4d6ebb00
parentb14118bbe9ad644f8e16279255fed61d399b96fa (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.go23
-rw-r--r--pkg/manager/repro.go15
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()