Coverage-Guided Fuzzing
Coverage-guided fuzzing evolves random inputs toward new code paths, using instrumentation feedback to mutate seeds that reach unexplored branches — beyond human test imagination.
Definition
Coverage-guided fuzzing runs the target with mutated inputs while instrumentation reports which code executed. Inputs reaching new coverage become seeds for further mutation, evolving a corpus that explores deep paths — parsers, decoders, protocol handlers — and crashes on invalid assumptions. American Fuzzy Lop pioneered the approach; libFuzzer and OSS-Fuzz industrialized it.
Context and scope
Applied continuously to parsers, codecs, crypto implementations, and protocol stacks. Structured fuzzing uses grammars or dictionaries; in-process fuzzing targets library APIs directly.
Common misconceptions
- Fuzzing replaces test design — it complements it; fuzzing finds the cases nobody thought to write, tests pin the cases that matter
- Random inputs only scratch the surface — feedback-driven mutation drives coverage deep; the classic one-liner inputs of pure random fuzzing are long obsolete
- Crashes are the only output — coverage growth, stability, and timeout reports are equally valuable signals about robustness
Anti-patterns
- Fuzzing black-box binaries without symbols or coverage and burning CPU on blind mutation
- Ignoring non-crashing hangs and OOM reports as noise when they are availability bugs
- Seeding with only empty and trivial inputs and starving the corpus of valid structure
Related concepts
Related examples
Authoritative source
What it is
Human-written test cases explore the paths humans imagine. The fuzzer enumerates the rest: it breeds inputs from coverage feedback, retaining mutants that discovered new branches and discarding the redundant.
How it works
Instrumentation (compile-time or binary translation) maps each input to a coverage signature. The engine mutates seeds — byte flips, splices, dictionary tokens — keeps inputs with novel signatures, and logs failures: crashes, hangs, OOM. Structured targets use grammar-aware mutators or protocol state models to generate valid-enough inputs that reach deep code.
When to deploy
Anything parsing untrusted bytes: file formats, network protocols, JWT and cookie parsing, image decoders, template engines. Run continuously on a corpus committed to the repository so each run inherits past discoveries.
Pitfalls
Coverage novelty saturates: growth flattens and the fuzzer idles — add dictionaries, grammar seeds, or structure-aware mutators to reopen paths. Crashes require minimization before they are actionable; a reproducer without a reduced case becomes triage debt. Deterministic builds matter — unreproducible binaries make crashes unfixable.