Structure-aware fuzzing can better exercise the system under test (SUT) by crafting inputs in the format expected by the SUT, rather than throwing pseudorandom bytes against it. That is, it avoids "shallow" inputs that the SUT will reject early (for example, syntactically invalid source text when fuzzing a programming language's compiler) and only produces inputs that go "deep" into the SUT (e.g. programs that type-check and exercise the mid-end optimizer and backend code generator). The Rust fuzzing ecosystem is largely built around
cargo-fuzzand thelibfuzzer-syscrate, which provides two methods for structure-aware fuzzing:
Generating structured inputs from scratch with the
arbitrarycrateMutating existing inputs from the fuzzer's corpus in a structure-aware manner, thereby producing new structured inputs, via the
fuzz_mutator!hookWhile the two methods are not technically mutually exclusive, combining the two can be difficult and engineering resources are finite. So:
If we are only implementing one approach, is generation or mutation better?
Full write up here: https://fitzgen.com/2026/06/01/structure-aware-fuzzing-experiment.html
arbhas 1.00 +/- 0.00 times more coverage thanbottom_up(p = 0.01)mutatehas 1.01 +/- 0.00 times more coverage thanarb(p = 0.00)top_downhas 1.00 +/- 0.00 times more coverage thanarb(p = 0.00)mutatehas 1.02 +/- 0.00 times more coverage thanbottom_up(p = 0.00)top_downhas 1.01 +/- 0.00 times more coverage thanbottom_up(p = 0.00)mutatehas 1.01 +/- 0.00 times more coverage thantop_down(p = 0.00)
bottom_uphas 1.01 +/- 0.01 times more coverage thanarb(p = 0.04)mutatehas 1.47 +/- 0.02 times more coverage thanarb(p = 0.00)top_downhas 1.06 +/- 0.02 times more coverage thanarb(p = 0.00)mutatehas 1.45 +/- 0.01 times more coverage thanbottom_up(p = 0.00)top_downhas 1.05 +/- 0.02 times more coverage thanbottom_up(p = 0.00)mutatehas 1.38 +/- 0.02 times more coverage thantop_down(p = 0.00)
# Run the benchmarks.
cargo run --bin benchmark -- path/to/output
# Analyze the benchmark data.
cargo run --bin analyze -- path/to/outputRun with --help to see all supported flags and options.