Skip to content

Commit 8566751

Browse files
committed
syz-cluster: share base kernel crashes between fuzzing sessions
Report base kernel crashes observed during fuzzing. Consult the common API for each patched kernel crash to see if it was already observed on the base kernel.
1 parent f1386b5 commit 8566751

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

pkg/manager/diff.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
type DiffFuzzerConfig struct {
4141
Debug bool
4242
PatchedOnly chan *UniqueBug
43+
BaseCrashes chan string
4344
Store *DiffFuzzerStore
4445
ArtifactsDir string // Where to store the artifacts that supplement the logs.
4546
// The fuzzer waits no more than MaxTriageTime time until it starts taking VMs away
@@ -51,6 +52,11 @@ type DiffFuzzerConfig struct {
5152
// trying to reach the modified code. The time is counted since the moment
5253
// 99% of the corpus is triaged.
5354
FuzzToReachPatched time.Duration
55+
// The callback may be used to consult external systems on whether the
56+
// base kernel has ever crashed with the given title.
57+
// It may help reduce the false positive rate and prevent unnecessary
58+
// bug reproductions.
59+
BaseCrashKnown func(context.Context, string) (bool, error)
5460
}
5561

5662
func (cfg *DiffFuzzerConfig) TriageDeadline() <-chan time.Time {
@@ -186,13 +192,13 @@ loop:
186192
log.Logf(0, "STAT %s", data)
187193
case rep := <-dc.base.crashes:
188194
log.Logf(1, "base crash: %v", rep.Title)
189-
dc.store.BaseCrashed(rep.Title, rep.Report)
195+
dc.reportBaseCrash(ctx, rep)
190196
case ret := <-runner.done:
191197
// We have run the reproducer on the base instance.
192198

193199
// A sanity check: the base kernel might have crashed with the same title
194200
// since the moment we have stared the reproduction / running on the repro base.
195-
crashesOnBase := dc.store.EverCrashedBase(ret.origReport.Title)
201+
crashesOnBase := dc.everCrashedBase(ctx, ret.origReport.Title)
196202
if ret.crashReport == nil && crashesOnBase {
197203
// Report it as error so that we could at least find it in the logs.
198204
log.Errorf("repro didn't crash base, but base itself crashed: %s", ret.origReport.Title)
@@ -218,7 +224,7 @@ loop:
218224
})
219225
}
220226
} else {
221-
dc.store.BaseCrashed(ret.origReport.Title, ret.origReport.Report)
227+
dc.reportBaseCrash(ctx, ret.origReport)
222228
log.Logf(0, "crashes both: %s / %s", ret.origReport.Title, ret.crashReport.Title)
223229
}
224230
case ret := <-dc.doneRepro:
@@ -254,6 +260,36 @@ loop:
254260
return g.Wait()
255261
}
256262

263+
func (dc *diffContext) everCrashedBase(ctx context.Context, title string) bool {
264+
if dc.store.EverCrashedBase(title) {
265+
return true
266+
}
267+
// Let's try to ask the external systems about it as well.
268+
if dc.cfg.BaseCrashKnown != nil {
269+
known, err := dc.cfg.BaseCrashKnown(ctx, title)
270+
if err != nil {
271+
log.Logf(0, "a call to BaseCrashKnown failed: %v", err)
272+
} else {
273+
if known {
274+
log.Logf(0, "base crash %q is already known", title)
275+
}
276+
return known
277+
}
278+
}
279+
return false
280+
}
281+
282+
func (dc *diffContext) reportBaseCrash(ctx context.Context, rep *report.Report) {
283+
dc.store.BaseCrashed(rep.Title, rep.Report)
284+
if dc.cfg.BaseCrashes == nil {
285+
return
286+
}
287+
select {
288+
case dc.cfg.BaseCrashes <- rep.Title:
289+
case <-ctx.Done():
290+
}
291+
}
292+
257293
func (dc *diffContext) waitCorpusTriage(ctx context.Context, threshold float64) chan struct{} {
258294
const backOffTime = 30 * time.Second
259295
ret := make(chan struct{})
@@ -343,7 +379,10 @@ func (dc *diffContext) NeedRepro(crash *Crash) bool {
343379
}
344380
dc.mu.Lock()
345381
defer dc.mu.Unlock()
346-
if dc.store.EverCrashedBase(crash.Title) {
382+
383+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
384+
defer cancel()
385+
if dc.everCrashedBase(ctx, crash.Title) {
347386
return false
348387
}
349388
if dc.reproAttempts[crash.Title] > maxReproAttempts {

syz-cluster/workflow/fuzz-step/main.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,33 +121,48 @@ func run(baseCtx context.Context, client *api.Client, timeout time.Duration,
121121

122122
eg, ctx := errgroup.WithContext(baseCtx)
123123
bugs := make(chan *manager.UniqueBug)
124+
baseCrashes := make(chan string, 16)
124125
eg.Go(func() error {
125126
defer log.Logf(0, "bug reporting terminated")
126127
for {
127-
var bug *manager.UniqueBug
128128
select {
129-
case bug = <-bugs:
129+
case title := <-baseCrashes:
130+
err := client.UploadBaseFinding(ctx, &api.BaseFindingInfo{
131+
BuildID: *flagBaseBuild,
132+
Title: title,
133+
})
134+
if err != nil {
135+
app.Errorf("failed to report a base kernel crash %q: %v", title, err)
136+
}
137+
case bug := <-bugs:
138+
err := reportFinding(ctx, client, bug)
139+
if err != nil {
140+
app.Errorf("failed to report a finding %q: %v", bug.Report.Title, err)
141+
}
130142
case <-ctx.Done():
131-
}
132-
if bug == nil {
133-
break
134-
}
135-
// TODO: filter out all INFO: bugs?
136-
err := reportFinding(ctx, client, bug)
137-
if err != nil {
138-
app.Errorf("failed to report a finding %s: %v", bug.Report.Title, err)
143+
return nil
139144
}
140145
}
141-
return nil
142146
})
143147
eg.Go(func() error {
144148
defer log.Logf(0, "diff fuzzing terminated")
145149
return manager.RunDiffFuzzer(ctx, base, patched, manager.DiffFuzzerConfig{
146150
Debug: false,
147151
PatchedOnly: bugs,
152+
BaseCrashes: baseCrashes,
148153
Store: store,
149154
MaxTriageTime: timeout / 2,
150155
FuzzToReachPatched: fuzzToReachPatched(),
156+
BaseCrashKnown: func(ctx context.Context, title string) (bool, error) {
157+
ret, err := client.BaseFindingStatus(ctx, &api.BaseFindingInfo{
158+
BuildID: *flagBaseBuild,
159+
Title: title,
160+
})
161+
if err != nil {
162+
return false, err
163+
}
164+
return ret.Observed, nil
165+
},
151166
})
152167
})
153168
const (

0 commit comments

Comments
 (0)