Safeguard
Security Guides•

Your fuzzer found nothing for days: is it plateaued?

A flat coverage line means the harness cannot reach more code, not that the code is clean. A checklist to diagnose a stalled fuzzer and the fixes to try.

Safeguard Security Team
3 min read

A fuzzer that runs for days with no new coverage and no crashes has probably plateaued. That does not prove the code is safe. It usually means something blocks progress: weak seeds, a checksum, a slow target, or code the harness never calls. Read the coverage report before running longer.

Confirm it is a plateau

The OSS-Fuzz FAQ says a fuzzer running for many days without new findings may have plateaued. Look at the counters: for libFuzzer the cov and ft fields in the status line, for AFL++ the total paths and cycles done. If they are flat, continue. If they still creep up, the run is just slow.

Check 1: is the target fast enough?

Look at executions per second. The Google guidance treats under 10 per second as a sign of trouble, and suggests about 1,000 per core as a typical goal. Slow work like logging, file access and large allocations drags the number down.

Check 2: what code is not reached?

Generate a coverage report. OSS-Fuzz documents a helper that builds a coverage configuration and produces a report from a corpus. cargo-fuzz has cargo fuzz coverage. Open the report and find the red lines. If the unreached code sits behind one validation function, you have found the blocker.

Check 3: is there a gate the fuzzer cannot pass?

Typical gates are checksums, signatures, length fields, magic numbers and compressed data. Fixes, from cheap to costly:

  1. Add seeds that already pass the gate.
  2. Add a dictionary with the needed tokens.
  3. Disable the check in a fuzz-only build, behind an unmistakable guard macro.
  4. Write a custom or structure-aware mutator.

Check 4: is the harness too narrow, or too broad?

If the target calls one function but the real bugs live in a neighbour, widen the entry point. If one target tries to parse five formats, split it, so each corpus focuses.

Check 5: are seeds redundant?

Run a corpus minimizer such as libFuzzer's merge mode or AFL++ cmin. A smaller varied corpus often helps. Add new real-world samples from bug reports and test suites.

Check 6: try engine features

libFuzzer has a value profile option, but the docs warn it can cost up to 2x speed and enlarge the corpus several times. AFL++ offers CmpLog and other variations for secondary instances. Run different configurations in parallel and compare coverage.

Check 7: stop

Sometimes the right answer is that the target is covered. Compare the coverage report to the code you care about. If the important code is reached and the fuzzer has run for a long while, move the effort to another target. Keep the corpus and replay it in CI.

Do not report "no findings" alone

When you tell a colleague or auditor the fuzzer found nothing, attach the coverage numbers and run time. Without them, the statement says very little.

Quick check

How long should I run a fuzzer before giving up?

There is no fixed rule. Judge by coverage growth and by whether the important code is reached, not by the clock alone.

Does zero crashes mean the code is bug free?

No. It means this harness, with these inputs, found none in this time.

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.