Safeguard
Security Guides•

How coverage-guided fuzzing works, in plain words

A coverage-guided fuzzer mutates inputs, tracks which code they reach, and keeps those that reach something new. Here is the loop and why it beats random.

Safeguard Security Team
4 min read

A coverage-guided fuzzer starts from a few sample inputs, changes them slightly, runs the program, and records which code ran. If an input reaches code no earlier input reached, the fuzzer keeps it and mutates it next. That feedback loop is what lets it get past early checks that pure random data never passes.

The loop in five steps

  1. Start with a corpus: a folder of small valid inputs, or even an empty one.
  2. Pick an input and mutate it: flip bits, insert bytes, splice two inputs together, overwrite a value with an interesting number.
  3. Run the target with the mutated input.
  4. Check coverage. LLVM describes libFuzzer as an "in-process, coverage-guided, evolutionary fuzzing engine", and the compiler instruments the code so the engine can see which blocks or edges ran.
  5. If the input hit new coverage, add it to the corpus. If the program crashed, save the input. Then repeat, millions of times.

The word evolutionary matters. Inputs that find new paths survive, and the corpus slowly grows into a set that exercises the program well.

Why random input is not enough

Imagine a parser that first checks that a file starts with a four byte magic number, then reads a length field, then parses the body. A random byte string fails the magic number check almost every time, so the body code is never touched.

A coverage-guided fuzzer solves this one step at a time. Some mutation happens to produce the first byte right, the program gets one branch further, coverage grows, and that input is kept. The next round builds on it. Each small win unlocks the next check.

Where it gets stuck

Feedback helps with simple comparisons, but some checks are hard to cross by mutation: checksums, lengths that must match content, compressed streams, and strict grammars. The Google fuzzing documentation gives the example of mutating raw bytes of a compressed stream, which makes nearly every input invalid for the decompressor. For those, people add dictionaries, better seed files, or structure-aware mutators, which have their own posts in this series.

Some engines add extra feedback. libFuzzer has a value profile option that treats new comparison argument values as new coverage. The docs warn it can slow things by up to 2x and grow the corpus several times larger, so it is a tool for stuck runs, not a default.

What the fuzzer is looking for

Coverage is the guide, but the goal is a failure signal. That signal is a crash, a timeout, an out-of-memory condition, or a sanitizer report. Without a sanitizer, a heap overflow may write past a buffer and carry on silently, and the fuzzer sees nothing. With AddressSanitizer on, the same input stops with a report. This is why fuzzing and sanitizers are almost always taught together.

What to expect from a run

A fuzzer has no natural end. libFuzzer runs indefinitely by default, and Go's tool says fuzzing could run indefinitely if it finds no errors. You decide the budget: a few minutes in a pull request, hours or days in a nightly job.

Early on you will see coverage jump fast, then slow down. A flat line for a long time usually means the harness cannot reach more code, not that the code is clean.

Quick check

Is a coverage-guided fuzzer the same as random testing?

No. Random testing ignores what happened last time. A coverage-guided fuzzer keeps inputs that reached new code and builds on them, which lets it get through multi-step checks.

Does reaching 100 percent coverage mean no bugs?

No. Coverage only says code ran. A bug may need a specific value or sequence that the inputs did not produce, and without a sanitizer some bugs do not fail visibly.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.