Safeguard
Best Practices

What is Fuzz Testing (Fuzzing)

Fuzz testing bombards software with malformed inputs to surface crashes and memory bugs. Here's how fuzzers work, what they've found, and where they fall short.

Shadab Khan
Security Engineer
7 min read

Fuzz testing, or fuzzing, is an automated software testing technique that bombards a program with malformed, unexpected, or massively randomized inputs to trigger crashes, memory corruption, and other exploitable failures before an attacker finds them first. Security researchers have used it since Barton Miller's "Fuzz" tool tested Unix utilities at the University of Wisconsin in 1988, but it became a mainstream defense after Google launched OSS-Fuzz in December 2016 and began running continuous fuzzing against open source infrastructure. As of early 2024, OSS-Fuzz has fuzzed more than 1,000 projects and surfaced over 10,000 vulnerabilities and 36,000 total bugs in libraries like OpenSSL, FFmpeg, and SQLite — many of them memory-safety issues that static analysis alone would never have caught. Fuzzing works because it doesn't require a human to imagine every malicious input; a fuzzer can execute tens of thousands of test cases per second and use code-coverage feedback to steer toward code paths no one has tested. For supply chain security teams, it remains one of the few techniques that reliably finds zero-day-class bugs before adversaries do.

What Is Fuzz Testing, Exactly?

Fuzz testing is a dynamic analysis method that feeds a target program a continuous stream of invalid or random data while monitoring for crashes, hangs, assertion failures, or sanitizer violations (like AddressSanitizer flagging a heap-buffer-overflow). Unlike static analysis, which reads source code without executing it, fuzzing actually runs the binary or function under test, which means it catches memory-safety bugs — buffer overflows, use-after-free, null-pointer dereferences — that only manifest at runtime. A single modern fuzzer instance, such as libFuzzer or AFL++, can execute 1,000 to 50,000 test cases per second depending on target complexity, meaning a 24-hour fuzzing job can churn through well over a billion inputs. Each crash is automatically minimized to the smallest input that still triggers it and deduplicated by stack trace, so a security engineer might review 40 unique crash signatures out of millions of raw executions rather than sifting through noise manually.

How Does a Fuzzer Actually Generate Its Inputs?

A fuzzer generates inputs through one of three approaches: mutation-based, generation-based, or coverage-guided, and most production fuzzers combine the last with one of the first two. Mutation-based fuzzers, like the original American Fuzzy Lop (AFL, released by Michal Zalewski in 2013), start from a corpus of valid seed files and flip bits, splice chunks, or insert byte sequences to create new test cases. Generation-based fuzzers instead build inputs from a grammar or protocol spec — useful for testing something like a JSON parser or an HTTP/2 stack where random byte-flipping rarely produces a syntactically valid packet. Coverage-guided fuzzing, popularized by AFL and adopted by libFuzzer (part of LLVM since 2015) and Google's ClusterFuzz, instruments the target binary so the fuzzer can see which code branches each input exercised; inputs that reach new branches get prioritized for further mutation, which is why AFL-style fuzzers can find deep bugs that pure black-box random testing misses in the same timeframe.

What Real Vulnerabilities Has Fuzzing Found?

Fuzzing has found some of the highest-impact vulnerabilities in modern software history, including bugs with CVSS scores at or near 9.8. CVE-2022-3602 and CVE-2022-3786, buffer overflows in OpenSSL's X.509 punycode decoding logic, were caught by OSS-Fuzz in late 2022 before public disclosure, avoiding what could have been a Heartbleed-scale event given OpenSSL's footprint. Google's syzkaller, a coverage-guided kernel fuzzer released in 2017, has found more than 5,000 bugs in the Linux kernel alone as tracked on its public dashboard, many of them in networking and filesystem code paths that had gone unaudited for over a decade. On the Linux kernel's mailing lists, syzbot (syzkaller's automated bug-reporting bot) files and triages dozens of new kernel crash reports every week. Heartbleed itself (CVE-2014-0160, disclosed April 2014) was in a codepath that later became a canonical fuzzing target, and its aftermath is a large part of why OpenSSL, LibreSSL, and BoringSSL are now continuously fuzzed rather than only manually reviewed.

What Are the Main Types of Fuzzers Security Teams Use?

The main fuzzer types are mutation-based byte fuzzers, coverage-guided fuzzers, grammar/protocol fuzzers, and kernel or hardware-level fuzzers, each suited to a different attack surface. AFL++ (a maintained fork of AFL first released in 2019) and libFuzzer remain the default choices for fuzzing C/C++ parsers, codecs, and compression libraries, and both integrate directly with OSS-Fuzz's build infrastructure. Peach Fuzzer and boofuzz specialize in stateful network protocol fuzzing — think SMB, MQTT, or proprietary TCP protocols — where the fuzzer needs to complete a handshake before it can mutate the payload. Jazzer (for Java) and Atheris (for Python), both released by Google's fuzzing team in 2021 and 2020 respectively, extend coverage-guided fuzzing to managed-memory languages by hooking the interpreter or JVM bytecode instead of relying on ASan-style native instrumentation. For firmware and kernel targets, syzkaller and AFL's QEMU mode simulate or emulate hardware so fuzzers can reach device driver code that never runs in userspace.

Where Does Fuzzing Fall Short?

Fuzzing falls short at finding business-logic flaws, authentication bypasses, and any vulnerability class that doesn't produce a crash or sanitizer violation the fuzzer can observe. A fuzzer can run a JSON parser a billion times and never notice that an authorization check is missing three functions away, because nothing about a missing permission check crashes the process. Fuzzing also depends entirely on harness quality: a poorly written harness that doesn't reset global state between iterations produces false crashes, while one that doesn't call the vulnerable function in a realistic way never reaches the bug at all — OSS-Fuzz's own documentation notes that harness bugs are one of the most common reasons a real vulnerability goes undetected for years despite continuous fuzzing. Coverage also plateaus: most fuzzing campaigns hit diminishing returns after reaching 60-80% line coverage on a given target because the remaining code is behind complex state machines or requires inputs the mutation engine can't construct without semantic knowledge of the format. That's why fuzzing is treated as one control among several — not a replacement for SAST, SCA, or manual review — and why a bug's fix still requires knowing whether the vulnerable function is even reachable from code your organization actually deploys.

How Does Fuzzing Fit Into a Modern Supply Chain Security Program?

Fuzzing fits into a modern supply chain program as the dynamic-testing layer that validates the memory-safety of open source components before and after they're pulled into a build. Most organizations don't run their own fuzzing infrastructure against every dependency — OSS-Fuzz alone covers roughly 1,000-1,300 projects, a small fraction of the millions of packages on npm, PyPI, and Maven Central — so security teams instead need to know which of the CVEs surfaced by upstream fuzzing campaigns actually affect their deployed code. A CVE found by libFuzzer in a rarely-used C extension of a Python package matters far less than one in a function your application calls directly on every request. This is where fuzzing results have to be correlated with your own SBOM and call-graph data, not treated as a flat severity score, because a 9.8 CVSS finding in unreachable code carries less real operational risk than a 6.5 in a hot path.

How Safeguard Helps

Safeguard turns fuzzing-derived CVEs — like the OSS-Fuzz and syzkaller findings referenced above — into prioritized, actionable work instead of an undifferentiated patch backlog. Our reachability analysis traces whether a vulnerable function found through fuzzing is actually invoked by your application's call graph, so teams stop burning cycles on CVEs in dead code paths. Griffin AI correlates those reachability signals with exploit maturity and package popularity to rank fixes by real risk, and it can draft auto-fix PRs that bump the affected dependency to the first patched version without breaking your build. Safeguard also generates and ingests SBOMs across your build pipeline, so when a new fuzzing-discovered CVE drops for a library like OpenSSL or a JSON parser, you already know every service that pulls it in — no manual grep across repos required.

Never miss an update

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