Repository navigation
ci: minimize a fuzz input for 1s, not 10s - #1385
Open
fuad-daoud wants to merge 4 commits into
Open
fuad-daoud wants to merge 4 commits into
fuad-daoud wants to merge 4 commits into
Conversation
Each search minimizes for 1s, not 10s: the flag also governs every input that adds coverage, and a worker minimizing runs no search. A search run by hand defaults to 5s. Measured with a fresh corpus each run, three runs a side, alternating, -fuzztime=1m -parallel 4, and coverage read from the corpus through GODEBUG=fuzzdebug=1, which reads 410 bits for a target's seeds alone against 854 after an 8s search: | Target | Coverage bits, 10s | Coverage bits, 1s | New inputs, 10s | New inputs, 1s | |-----------------------------------------|-------------------:|------------------:|----------------:|---------------:| | FuzzCycleDetector | 40,006-41,090 | 42,402-42,766 | 33-71 | 240-269 | | FuzzNewBigVal_AcceptedFormsAreJSONValid | 930-989 | 997-1,020 | 124-152 | 137-151 | The cost a shorter bound could have is a larger reproducer, and it did not appear: over four runs a side on a planted crash, reproducers were 2 or 3 bytes at both bounds, and 10s missed the crash once within 2 minutes. This compares one shorter bound on two targets. At a 10s budget across all 15 targets, 1s also found 1,035 new inputs against 863, and a search run by hand now defaults to 5s, which found 812. Split from 22cd99d, whose other changes move the search to a nightly job.
The comment over minimize_time said a shorter bound costs at most a larger reproducer. The flag also bounds the minimizing of every new input that adds coverage, and Go keeps an input whose minimizing is cut short as it stands, so a shorter bound can leave each new corpus entry less minimal too. Say so, so the next reader knows what the bound trades.
The comment states what Go's fuzzing engine does with -fuzzminimizetime: it minimizes every coverage-adding input, not only a crash, and a worker that is minimizing runs no search. Name where that was read, as the workflow header does for the cache's trim: go1.27's internal/fuzz, fuzz.go's queueForMinimization and worker.go's loop.
fuad-daoud
marked this pull request as ready for review
November 2, 2026 23:28
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Each search minimizes a new input for 1s, not 10s (
-fuzzminimizetime). The flag governs every input that adds coverage, not only a crash, and a worker that is minimizing runs no search. A shorter bound can leave a crash's reproducer and each new corpus entry less minimal, a cost the measurement weighed: new inputs and coverage bits both rose at 1s. A search run by hand now defaults to 5s a target (fuzz.sh, theMakefilehelp andFUZZTIME, and the README). The nightly job'sFUZZTIME: 1mis unchanged, and so is its timeout estimate:-fuzztime's deadline cancels minimizing too (go1.27internal/fuzz/fuzz.go: the workers' context is a child of the-fuzztimetimeout), so the bound never lengthens a target's search.The 5s default is a second decision, carried here with the bound from the same commit. Once #1382 lands, the nightly job does the deep search, and a search run by hand is a quick local check before a push, so 5s halves its wait: about 75s of search over 15 targets instead of 150s. By this body's own figures it costs about a fifth of the new inputs such a search finds, 812 at 5s against 1,035 at 10s, both at the 1s bound.
Stacked on #1382, which moves the search to a nightly job and is stacked on #1373 and #1311; the diff here shows only this change. The stack lands bottom-up: once #1382 merges, this is retargeted to
main(the repository does not delete merged branches, so GitHub will not retarget it), andCloses #1290links there. This is split from Omar's22cd99d0(#1301), whose other changes are in #1382. Omar'sd16302accarries his author and date and his measurement in its message;a4dd4b41then names the corpus cost in thefuzz.shcomment, andfe26eda1cites the Go source it rests on.Why 1s
Fresh corpus each run, three runs a side, alternating,
-fuzztime=1m -parallel 4, coverage read from the corpus throughGODEBUG=fuzzdebug=1, which reads 410 bits forFuzzNewBigVal_AcceptedFormsAreJSONValid's seeds alone against 854 after an 8s search:10s1s10s1sFuzzCycleDetectorFuzzNewBigVal_AcceptedFormsAreJSONValidThe cost #1290 names did not appear. Over four runs a side on a planted crash, reproducers were 2 or 3 bytes at both bounds, and 10s missed the crash once within 2 minutes. At a 10s budget across all 15 targets, 1s also found 1,035 new inputs against 863, and a search run by hand at the new 5s default found 812. This compares one shorter bound on two targets, plus the 15-target totals.
A second look at
FuzzNewBigVal_AcceptedFormsAreJSONValidon this branch, two runs a side,-fuzztime=1m -parallel 4, same method: coverage bits 943 and 950 at 10s, 984 and 973 at 1s. New inputs 120 and 113 at 10s, 126 and 136 at 1s.Out of scope
A deadline that reports no finding (#466) is left alone, as #1290 puts it.
Before and after
flowchart LR subgraph before["Before"] direction TB b1["a search finds a new input"] --> b2["a worker minimizes it for up to 10s<br/>and searches nothing meanwhile"] b2 --> b3["at a 10s budget, as long as the search itself"] end subgraph after["After"] direction TB a1["a search finds a new input"] --> a2["minimized for up to 1s"] a2 --> a3["more of each budget spent searching:<br/>more new inputs and coverage bits"] a2 -. "cost" .-> a4["reproducers and corpus entries<br/>can be less minimal"] end before ~~~ afterWhat it brings: each night's minute a target, and each search run by hand, spends its time finding inputs rather than shrinking them: on the measured targets 1s found more new inputs and more coverage than 10s.
Test plan
gowrapper onPATHthat logsgo testarguments, then./scripts/fuzz.shwithFUZZTIMEunset: 15go testlines, each-fuzztime=5s -fuzzminimizetime=1s.FUZZTIME=1m: 15 lines, each-fuzztime=1m -fuzzminimizetime=1s;make -n fuzzprints./scripts/fuzz.sh 5s.minimize_time=10s: the log shows-fuzzminimizetime=10son all 15 lines. Reverted.bash -nandshellcheck scripts/fuzz.sh;make fuzz-check.make gate: this pull request's CI runs it on this head. No Go changed.Closes #1290
Related: #1268, #1273, #1301, #1382