Join us in our live webinar, AI Agent Security: The Good, The Bad, and The Ugly on May 8th 2025, 9 AM PT

The Role of Fuzz Testing in Software Security Part 1

PUBLISHED:
April 23, 2025
|
BY:
Debarshi Das
Ideal for
Pentester
Developer

Picture this: You’ve written a C library to parse complex file formats. You’ve meticulously reviewed the code, run it through static analyzers like Coverity and PVS Studio, and fixed every warning. 

Confident in its robustness, you deploy it. Months later, a malformed input triggers a heap overflow, leading to a critical remote code execution vulnerability.

This scenario is not hypothetical. It’s a recurring theme in software built with memory-unsafe languages like C/C++. 

Static Application Security Testing (SAST) tools are indispensable, but they operate in a vacuum, analyzing code without executing it. To uncover vulnerabilities that lurk in runtime behavior, you need a dynamic approach: fuzzing.

Table of Contents

  1. Why Static Analysis isn’t Enough
  2. What is Fuzzing?
  3. Evidence that Fuzzing Works
  4. How To Start Fuzzing C/C++ Codebases
  5. Embracing the Chaos

Why Static Analysis isn’t Enough

SAST tools are great! They’re like spellcheck for code. They’ll tell you that scanf() might be unsafe, or that your malloc() is missing a free(). 

But here’s the problem: they can’t see runtime behavior. Besides this they suffer from two burdening issues:

  • Flood of false positives: SAST tools are infamous for flooding vulnerability management systems with false positives of unsafe functions or, blatantly ignoring safety checks. 
  • False negatives in complex logic: Bugs often arise from interactions between functions, modules, or even threads scenarios which SAST struggles to model. For example, it might flag a safe strcpy(dest, src) as risky but may not always determine if strncpy(dest, src, len) is supplied arbitrary length.

Take FreeRadius, the most widely used RADIUS server in the world powering major Internet Service Providers and telecommunications companies. For years, they relied on secure coding standards and industry-standard SAST tools like Coverity, PVS Studio, cpp-check, and others. 

Then, in 2017, they got fuzzed. The result? A parade of vulnerabilities they never knew existed like buffer overflows triggered by malformed packets. SAST had missed them because the bugs only surfaced when the code was executed with specific inputs.

SAST is like checking if your car has seatbelts. Fuzzing is like crashing it into a wall to see if the airbags work.

What is Fuzzing?

Fuzzing or fuzz testing is an automated dynamic analysis technique designed to discover bugs by injecting invalid, unexpected, or random inputs into a program. 

Unlike SAST which just inspects source code, fuzzing tracks how the software behaves during execution.

Modern fuzzers operate in three key phases:

  1. Input Generation:
    • Mutation-Based Fuzzers (e.g., AFL, libFuzzer): Start with valid seed inputs (e.g., a PNG file) and mutate them using strategies like bit-flipping, arithmetic increments, or block splicing.
    • Generational Fuzzers (e.g., Peach Fuzzer): Use models of the input format (e.g., protocol specifications) to generate structured test cases.
  1. Execution and Monitoring: The target program processes each input while the fuzzer monitors for crashes, memory leaks, or undefined behavior using sanitizers (e.g., ASan, UBSan).
  1. Feedback-Driven Optimization: Coverage-guided fuzzers (e.g., AFL++) map code coverage to prioritize inputs that explore new execution paths, creating a feedback loop to maximize the attack surface.

Fuzzing is not random. It’s a systematic exploration of your program’s attack surface, which uncovers edge cases that human auditors and SAST tools overlook.

“But My Code is Perfect!” – John, A Developer From a Major Company Currently Experiencing a Zero-Day Exploit

While John is being promoted from developer to customer by his manager, let’s crush denialism with some statistics and evidence:

  • OpenSSL: While Heartbleed remains infamous, OpenSSL continues to rely on fuzzing to prevent regressions. 
    • In 2022, OSS-Fuzz discovered CVE-2022-3602 and CVE-2022-3786, two critical buffer overflow vulnerabilities in OpenSSL’s punycode parsing logic. These flaws could have enabled remote code execution but were caught before they reached production, thanks to continuous fuzzing.
  • Microsoft’s Windows: 
    • In 2021, fuzzing uncovered a critical remote code execution (RCE) bug in the Windows TCP/IP driver (CVE-2021-24086), which could have allowed attackers to take over unpatched systems via malicious network packets.
    • In 2023, Azure’s Kubernetes ingress-nginx component fixed a memory corruption flaw (CVE-2023-39418) found through libFuzzer.
  • Curl: The ubiquitous Curl, used by billions of devices, has integrated fuzzing via OSS-Fuzz since 2017.
    • CVE-2021-22901: A use-after-free vulnerability in Curl’s TLS handshake logic, found by OSS-Fuzz. Exploitable for RCE.
    • CVE-2023-38545: A heap-based buffer overflow in Curl’s SOCKS5 proxy handling, discovered by a custom fuzzing harness.
  • Google’s OSS-Fuzz: Since its launch in 2016, OSS-Fuzz (Google’s free fuzzing service for open-source projects) has:
    • Fuzzed 1,000+ critical open-source projects (Linux, Kubernetes, PHP, Python, etc.).
    • Discovered 10,000+ vulnerabilities (including 4,000+ labeled high/critical severity).
    • Prevented 36,000+ bugs (memory leaks, undefined behavior) from reaching production.
  • Linux Kernel: Google’s Syzkaller has uncovered 4,500+ bugs in the Linux kernel since 2015, with 30% classified as high-severity.
    • CVE-2023-0386 (2023): A privilege escalation flaw in the Linux OverlayFS subsystem.
    • CVE-2024-1086 (2024): A use-after-free vulnerability in Netfilter, leading to RCE.

The data is clear. From Curl to the Linux kernel, fuzzing has become the most effective way to uncover memory corruption flaws in C/C++ codebases.

How To Start Fuzzing C/C++ Codebases

Now that you’re convinced about the benefits of fuzzing, here’s some homework till the next post:

  • Choose a fuzzing framework:
    • AFL++: Ideal for binary targets.
    • libFuzzer: Integrated with Clang, perfect for library functions.
    • OSS-Fuzz: Google’s free service for open-source projects.
  • Instrument your code: Compile with sanitizers and coverage tracking.
"

$(CC) $(CFLAGS) -fsanitize=address,undefined -fno-omit-frame-pointer -g lib.c  
  
"
  • Write a harness: Create a simple function that feeds input data to your API:
"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {  
    MyParser parser;  
    parser.parse(data, size);  
    return 0;  
}
  
"
  • Run and Iterate: Let the fuzzer run for hours or days. Triage the crashes, fix the bugs, and expand the seed corpus.

This is an overview of a standard fuzzing campaign. It’s not always this straightforward. The real challenge in a fuzzing campaign is writing the harness and ensuring best coverage. 

In the next series of posts, we’ll explore the internals of a fuzzer and practise fuzzing hands-on by writing some harnesses for prominent and complex open-source software.

Embrace the Chaos

Fuzzing isn’t a silver bullet or a replacement for SAST or code reviews. It’s a complementary force multiplier. 

By dynamically probing your code’s execution paths, it exposes vulnerabilities that static analysis cannot see. For projects in memory-unsafe languages like C/C++, it’s a critical line of defense against exploits that could lead to breaches, data loss, or regulatory penalties.

In Part 2, we’ll explore the internals of a fuzzer and some history of fuzzing. In part 3 we’ll get hands-on with advanced techniques, writing effective harnesses for stateful systems, integrating fuzzing into CI/CD, and interpreting crash reports to diagnose root causes.

Remember that fuzzing is your strongest weapon in the fight against memory corruption, but it's not a universal solution. Security begins with developer education and a commitment to secure-by-design principles, and AppSecEngineer is your best-friend in this endeavour.

Debarshi Das

Blog Author
Hey, I’m Debarshi—vulnerability researcher, reverse engineer, and part-time digital detective. My life’s mission? Breaking things (ethically, of course) and figuring out how they tick. If there’s a sneaky bug hiding in your code, I’ll hunt it down like a cybersecurity bloodhound.

Ready to Elevate Your Security Training?

Empower your teams with the skills they need to secure your applications and stay ahead of the curve.
Get Started Now
X
X
Copyright AppSecEngineer © 2025