From a68fda52c366653ed73c240a6b9c3f4e750ccdfd Mon Sep 17 00:00:00 2001 From: Grigory Bazilevich Date: Wed, 11 Mar 2026 09:42:40 +0300 Subject: pkg/fuzzer,pkg/corpus: detection and preservation of programs with probability coverage --- pkg/fuzzer/cover.go | 8 ++++++++ pkg/fuzzer/fuzzer.go | 12 +++++++----- pkg/fuzzer/job.go | 32 +++++++++++++++++++++++++------- 3 files changed, 40 insertions(+), 12 deletions(-) (limited to 'pkg/fuzzer') diff --git a/pkg/fuzzer/cover.go b/pkg/fuzzer/cover.go index e2dca3d88..191ff1491 100644 --- a/pkg/fuzzer/cover.go +++ b/pkg/fuzzer/cover.go @@ -36,6 +36,14 @@ func (cover *Cover) addRawMaxSignal(signal []uint64, prio uint8) signal.Signal { return diff } +func (cover *Cover) addMaxSignal(signal signal.Signal) signal.Signal { + cover.mu.Lock() + defer cover.mu.Unlock() + diff := cover.maxSignal.MergeDiff(signal) + cover.newSignal.Merge(diff) + return diff +} + func (cover *Cover) CopyMaxSignal() signal.Signal { cover.mu.RLock() defer cover.mu.RUnlock() diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go index bd55ca9c6..3241cb63a 100644 --- a/pkg/fuzzer/fuzzer.go +++ b/pkg/fuzzer/fuzzer.go @@ -24,8 +24,9 @@ import ( type Fuzzer struct { Stats - Config *Config - Cover *Cover + Config *Config + Cover *Cover + ProbCover *Cover ctx context.Context mu sync.Mutex @@ -50,9 +51,10 @@ func NewFuzzer(ctx context.Context, cfg *Config, rnd *rand.Rand, } } f := &Fuzzer{ - Stats: newStats(target), - Config: cfg, - Cover: newCover(), + Stats: newStats(target), + Config: cfg, + Cover: newCover(), + ProbCover: new(Cover), ctx: ctx, rnd: rnd, diff --git a/pkg/fuzzer/job.go b/pkg/fuzzer/job.go index bbac544f6..1f450ff7e 100644 --- a/pkg/fuzzer/job.go +++ b/pkg/fuzzer/job.go @@ -93,7 +93,9 @@ type triageCall struct { // Filled after deflake: signals [deflakeNeedRuns]signal.Signal + probSignal signal.Signal stableSignal signal.Signal + newProbSignal signal.Signal newStableSignal signal.Signal cover cover.Cover rawCover []uint64 @@ -162,12 +164,16 @@ func (job *triageJob) run(fuzzer *Fuzzer) { } func (job *triageJob) handleCall(call int, info *triageCall) { - if info.newStableSignal.Empty() { + skip := true + skip = skip && info.newStableSignal.Empty() + skip = skip && (info.newProbSignal.Empty() || info.newProbSignal.Len() < 4 && info.newProbSignal.Metric() < (1<<2)) + + if skip { return } p := job.p - if job.flags&ProgMinimized == 0 { + if job.flags&ProgMinimized == 0 && !info.newStableSignal.Empty() { p, call = job.minimize(call, info) if p == nil { return @@ -209,11 +215,13 @@ func (job *triageJob) handleCall(call int, info *triageCall) { } job.fuzzer.Logf(2, "added new input for %v to the corpus: %s", callName, p) input := corpus.NewInput{ - Prog: p, - Call: call, - Signal: info.stableSignal, - Cover: info.cover.Serialize(), - RawCover: info.rawCover, + Prog: p, + Call: call, + ProbOnly: info.newStableSignal.Empty(), + Signal: info.probSignal, + StableSignal: info.stableSignal, + Cover: info.cover.Serialize(), + RawCover: info.rawCover, } job.fuzzer.Config.Corpus.Save(input) } @@ -290,6 +298,16 @@ func (job *triageJob) deflake(exec func(*queue.Request, ProgFlags) *queue.Result deflakeCall(-1, result.Info.Extra) } job.info.Logf("deflake complete") + for call, info := range job.calls { + for i := range needRuns - 1 { + info.probSignal.Merge(info.signals[i].CopyWithPrio(uint8(i) << 2)) + } + info.newProbSignal = job.fuzzer.ProbCover.addMaxSignal(info.probSignal) + job.info.Logf("call #%d [%s]: |probability signal|=%d, |new probability signal|=%d%s", + call, job.p.CallName(call), info.probSignal.Len(), info.newProbSignal.Len(), + signalPreview(info.newProbSignal)) + } + for call, info := range job.calls { info.stableSignal = info.signals[needRuns-1] info.newStableSignal = info.newSignal.Intersection(info.stableSignal) -- cgit mrf-deployment