TL;DR: AFL++ campaigns can generate billions of executions and thousands of crashes while still yielding only a handful of real bugs, according to Appknox, because crash volume, coverage, and execution speed do not equal vulnerability discovery. The lesson is that fuzzing pipelines need deduplication, root-cause clustering, and triage discipline before output can be treated as security signal.
NHIMG editorial — based on content published by Appknox: AFL++ at Scale: Why crash volume doesn’t equal vulnerabilities
By the numbers:
- Appknox reports that 357 crash files were distilled into 2 actual bugs after triage and deduplication.
- Appknox says persistent mode improved throughput to approximately 394 executions per second per instance.
Questions worth separating out
Q: How should security teams turn fuzzing crashes into actionable risk signals?
A: Security teams should deduplicate crashes by root cause, reproduce them with memory-safety instrumentation, and cluster stack traces before reporting findings.
Q: Why do fuzzers generate so many crashes without finding many real bugs?
A: Fuzzers can reach the same defect through many input mutations, so one underlying issue appears as multiple crash files.
Q: What do teams get wrong about persistent mode in AFL++?
A: Teams often treat persistent mode as a pure speed upgrade, but it only works when the harness resets state correctly between iterations.
Practitioner guidance
- Add crash deduplication before vulnerability reporting Cluster AFL++ crashes by stack trace and reproduce them with ASAN before counting them as unique findings.
- Use persistent mode only with state-resetting harnesses Implement persistent loops so the target stays in memory across iterations, but reset allocated state, buffers, and parser context on every pass.
- Mix mutation schedules across secondary instances Assign different power schedules such as explore, exploit, rare, and one MOpt instance so parallel fuzzers do not converge on the same mutation path.
What's in the full article
Appknox's full blog covers the operational detail this post intentionally leaves for the source:
- Step-by-step AFL++ harness setup, including the native, ASAN, and CmpLog build matrix used in the campaign
- Concrete command-line examples for CLI validation, persistent mode, and mixed secondary schedules
- Crash triage workflow details showing how CASR, stack clustering, and reproduction reduced 1,166 crashes to 2 bugs
- Phase-by-phase performance and corpus growth numbers for teams benchmarking their own fuzzing pipelines
👉 Read Appknox's analysis of AFL++ fuzzing at scale and crash triage →
AFL++ crash triage: what practitioners should do with crash noise?
Explore further
Crash triage is a governance control, not a cleanup task. The article shows that fuzzing output becomes meaningful only when organisations separate duplicate crashes from unique defects. In practice, that means crash deduplication, stack clustering, and reproducible validation are part of the security control set, not a post-processing convenience. Teams that stop at crash counts are measuring activity, not exposure.
A question worth separating out:
Q: How do security teams decide whether to expand fuzzing coverage or tune throughput?
A: Expand coverage when crash volume rises but unique root causes plateau, because that pattern usually means you are retesting the same surface. Tune throughput when the harness still under-exercises the target. The right sequence is validation, persistent optimisation, then surface expansion, with triage running throughout.
👉 Read our full editorial: AFL++ crash triage shows why noise is not risk