Security Engineering

Fuzzing: How Throwing Garbage at Code Finds the Bugs Reviews Miss

July 7, 2026 8 min read Haven Team

A human reviewer reads code the way its author intended it to be used. That is exactly the blind spot attackers exploit, because attackers do not send the input you expected. They send a message with a length field that lies, a certificate with a field nested ten thousand levels deep, an image whose header claims a size that overflows a counter. Fuzzing is the discipline of finding those inputs before an attacker does, by having a machine generate millions of them and watching for the moment something breaks.


The idea is almost embarrassingly simple. Take a program that reads input, feed it a flood of malformed, random, or mutated data, and watch to see whether it crashes, hangs, or trips a memory-safety check. Every crash is a lead, because a crash on attacker-controllable input is the visible tip of a bug that might be exploitable. The name comes from a 1988 University of Wisconsin project where researchers fed random noise to Unix utilities and were startled by how many fell over. Nearly four decades later, fuzzing is one of the most productive bug-finding techniques in existence, and it has grown far beyond random noise.

Why parsers are the danger zone

Fuzzing pays off most on code that turns untrusted bytes into structured data. That means parsers: the code that reads an email, decodes an image, interprets a network packet, or unpacks a certificate. These live directly on the boundary between the outside world and your program's internals, and they are notoriously hard to get right because the input format has many edge cases and the code that handles them is often written in memory-unsafe languages.

When a parser written in C or C++ mishandles a malformed input, the consequence is frequently a memory-safety bug: a buffer overflow, a use-after-free, an out-of-bounds read. Those are the bug classes that turn "the program crashed" into "the attacker runs their own code." A parser that can be reached with data an attacker controls, and that mishandles hostile input, is one of the highest-value targets in any system.

The attacker's advantage

Tests written by developers check that valid input produces correct output. Attackers never send valid input. Fuzzing closes the gap by generating the invalid, the malformed, and the deliberately absurd, which is the exact territory tests tend to skip.

From random noise to coverage guidance

Early fuzzers were "dumb": they threw fully random bytes and hoped. That finds shallow bugs but wastes almost all its effort, because random data almost never gets past the first few validity checks in a parser, so the deep code never runs. The breakthrough that made modern fuzzing so effective is coverage guidance.

A coverage-guided fuzzer instruments the target program so it can see which code paths each input reaches. It keeps a pool of inputs, mutates them, and whenever a mutation reaches a new path it did not see before, it saves that input as a new seed to mutate further. Over time the fuzzer effectively learns the input format by feeling its way deeper into the code, one new branch at a time, without ever being told the format's rules. American Fuzzy Lop, released in 2013, popularized this approach, and libFuzzer, built into the LLVM compiler toolchain, made it easy to fuzz a single function in isolation.

Approach How it generates input Strength
Dumb / random Random bytes, no feedback Trivial to set up; finds only shallow bugs
Mutation-based Small changes to real sample inputs Reaches valid-looking structure quickly
Coverage-guided Mutations kept when they reach new code paths Learns its way into deep code; the modern default
Structure-aware Generates inputs that respect the format's grammar Gets past strict validity checks like checksums

Sanitizers make bugs loud

A fuzzer can only act on failures it can detect, and many dangerous bugs do not crash on their own. An out-of-bounds read might quietly return the wrong byte instead of crashing, and the fuzzer would never notice. Sanitizers fix that. AddressSanitizer, compiled into the target, turns a silent memory violation into an immediate, loud abort the moment it happens, so the fuzzer catches it. Companion tools catch use of uninitialized memory and undefined behavior. Pairing a coverage-guided fuzzer with sanitizers is what makes the technique find subtle corruption rather than only hard crashes.

The scale this reaches is worth stating plainly. Google's OSS-Fuzz service, launched in 2016, continuously fuzzes hundreds of important open-source projects on donated infrastructure and has reported many thousands of bugs across the software the internet runs on. Continuous fuzzing has become a baseline expectation for serious security-critical libraries rather than a one-time audit step.

A code review asks whether the code is correct for the inputs you imagined. A fuzzer asks what the inputs you did not imagine will do. Attackers work in the second question, so defenders have to as well.

The Heartbleed lesson

Heartbleed, the 2014 flaw in OpenSSL that let an attacker read chunks of a server's memory, is a useful case. It was a parser trusting an attacker-supplied length field, exactly the shape fuzzing is good at catching. Researchers later demonstrated that coverage-guided fuzzing, combined with a memory sanitizer, could rediscover the bug automatically. The uncomfortable takeaway was that a widely deployed, heavily relied-upon library shipped a memory-disclosure bug that a modern fuzzing setup would likely have surfaced beforehand. Much of the industry's investment in continuous fuzzing since then traces directly to that realization.

What this means for the software you trust

You do not run fuzzers to use a messenger or an email client, but the question of whether the software you rely on has been fuzzed is a fair one to ask, because the answer says something about how seriously its authors treat the untrusted-input boundary. Two engineering choices matter most here, and they compound.

Both of these are part of how Haven approaches the code that handles untrusted input, which is why security-sensitive parsing has been moved onto a memory-safe foundation. Fuzzing and memory safety are not competitors. They are two halves of the same defense: one hunts for the inputs that break your assumptions, the other limits how much damage a broken assumption can do. The goal, as always, is to reduce how much you have to trust that any single piece of code is perfect.

Try Haven free for 15 days

Encrypted email and chat in one app. No credit card required.

Get Started →