Skip to content
Jochen Hilgers11 min read

How to Secure Coding in C and C++ Using Fuzz Testing

Let me show you a simplified fuzz testing approach that enables secure coding of C and C++ applications. If you read this article to the end, you will learn about an AI-automated application security testing approach for C/C++ that can protect your applications against all sorts of memory corruptions and other common C/C++ vulnerabilities.

 

  What to Expect in This Article

Why secure C/C++

C and C++ remain widely used across critical industries due to their performance, efficiency, and low-level system access. These languages are the backbone of:

  • Operating Systems (Windows, Linux, macOS)
  • Automotive Software (ECU firmware, ADAS, vehicle communication protocols)
  • Medical Devices (life-support systems, imaging software, diagnostics)
  • Aerospace & Defense (real-time control systems, avionics software)
  • Financial Services (high-frequency trading systems, cryptographic libraries)
  • Embedded & IoT Systems (industrial control systems, consumer electronics)

At the same time, C and C++ are notorious for being bug-prone. In fact, one in four issues of the Top 25 Most Dangerous Software Weaknesses is related to C or C++.  

Memory corruption bugs remain among the most dangerous vulnerabilities:

  • One caused the CrowdStrike IT Outage in 2024. 
  • Three — Out-of-Bounds Write, Out-of-Bounds Read, and Use After Free — are in the Top 10 Most Dangerous Weaknesses. 

Even though most companies implement security practices such as linters, static analysis, and penetration testing, many critical vulnerabilities and bugs still make it into production. 

In this white paper, we explore how C/C++ code can be secured using an advanced security technique — white-box fuzz testing.

What is Fuzz Testing?

Fuzz testing is one of the most effective ways to find runtime bugs and security issues in C/C++ software. But let’s start with the basics. What is it? 

Fuzz Testing (aka fuzzing) is a dynamic testing method for finding functional bugs and security issues in software. During a fuzz test, a program or a function under test is executed with thousands of invalid, unexpected, or random inputs in order to crash the application, like a stress test for your application code.

White-box fuzzing

When fuzzing is employed in a white-box manner (meaning it has access to your source code so that it can instrument it), the fuzzer gets detailed feedback on the code’s structure and the states reached by the automatically generated inputs. 

The fuzzing engine actively uses this feedback to guide its mutations, creating new test cases that maximize code coverage and trigger new behaviors. This significantly increases the likelihood of finding bugs and exposing hidden vulnerabilities. You can learn more about fuzzing here.

How Fuzzing Helps With С/С++ Security

By feeding software with unexpected or invalid inputs, modern fuzzing tools can uncover dangerous edge cases and behaviors related to communication with memory, such as memory corruption bugs. These include buffer overflows, memory leaks, and other issues relevant to memory-unsafe languages such as C/C++. 

A few real-world results of using white-box fuzzing : 

  • Engineers at Google have already found more than 25,000 bugs in Chrome. 75% of these vulnerabilities were memory issues, such as heap buffer overflows and use-after-free. 
  • Microsoft found more than 1,800 bugs in their Office products. 
  • Automotive supplier Continental found 57% of bugs through fuzzing. 

What Industries Are Adopting Fuzzing?

Fuzz Testing has proven to be effective in testing critical software where security is a must. Industries such as automotive, medical devices, defense and aerospace, and industrial automation and control systems have adopted fuzzing to eliminate critical bugs in their C/C++ code.

In these industries, fuzzing is considered a state-of-the-art technology, and many standards advocate for its use.

 Automotive 

 Medical Devices

 Industrial Automation and Control Systems 

 Aviation

  • ISO/SAE 21434 Road Vehicles — Cybersecurity Engineering
  • Automotive SPICE for Cybersecurity Guidelines
  • Cybersecurity in Medical Devices: Quality System Considerations and Content of Premarket Submissions by the U.S. Food and Drug Administration (FDA)
  • AAMI TIR 57:2016 Principles For Medical Device Security - Risk Management
  • Guidance on cybersecurity for medical devices (MDCG 2019-16) by the European Commission and the Medical Device Coordination Group
  • IEC 81001-5-1 Health software and health IT systems safety, effectiveness and security. Part 5-1: Security — Activities in the product life cycle.
  • ISA/IEC 62443-4-1Security for industrial automation and control systems – Part 4-1: Secure product development lifecycle requirements  
  • ED-203A / DO-356A Airworthiness Security Methods and Considerations

 

Good Use Cases for Fuzz Testing

In general, any library or application that handles untrusted or complicated data is well suited for fuzz testing. For example:

  • Media Codecs
  • Network Protocols, RPC Libraries
  • Crypto
  • Compression
  • Compilers and Interpreters
  • Regular Expression Matchers
  • Text/UTF processing
  • Databases
  • Browsers
  • Text Editors/Processors
  • Embedded System
  • OS Kernel, Drivers, Supervisors and VMs

What Bugs Can You Find With Fuzzing in C/C++? 

With fuzzing, you can find all sorts of C/C++ vulnerabilities that can cause your application to crash. For example:

Resource Usage Bugs

Logical Bugs

  • Discrepancies between two implementations of the same protocol
  • Round-trip consistency bugs (e.g. compress the input decompress back, compare with the original)

Plain Crashes

  • NULL deference
  • Uncaught Exceptions
  • Segmentation Faults
  • Failed Assertions

Memory Issues

  • Buffer Overflows
  • Use After Free
  • Out of Bounds
  • Memory Leaks
  • Uninitialized reads

How to Trigger Memory Corruptions With Fuzzing? 

Sanitizers and white-box fuzzing work together to maximize bug detection. White-box fuzzing generates and mutates test inputs to explore deep execution paths, while sanitizers (e.g., ASan, UBSan) detect memory corruption and undefined behavior during execution.

Fuzzing increases code coverage, helping sanitizers catch more bugs, while sanitizers provide real-time feedback, allowing the fuzzer to refine inputs and uncover even more vulnerabilities. Together, they create a powerful, automated security testing approach.

With fuzzing, you can also find out-of-bounds errors, all kinds of buffer overflows, and other memory allocation errors, but you may have to use specific settings for the used compiler.

As an example, if a fuzzer generates inputs that exceed the allocated buffer size, these inputs do not necessarily cause the application to crash since accessing information in the unallocated area might well be allowed.

So, to reliably trigger memory corruptions during a fuzz test, you will need to restrict the program execution by applying sanitizers to your source code. 

What is a Sanitizer? 

Sanitizers are runtime bug detectors that monitor the code and report any issues that are triggered during runtime. The idea behind sanitizers is to trigger crashes when normal application behavior would not do that.

List of Sanitizers for Fuzzing

Here is a list of four common C/C++ sanitizers and the vulnerabilities you can find with them if you apply those sanitizers in combination with fuzzing: 

1. AddressSanitizer (ASan) is a memory error detector for C/C++. It finds:
  • Use after free (dangling pointer dereference)
  • Heap buffer overflow
  • Stack buffer overflow
  • Global buffer overflow
  • Use after return
  • Use after scope
  • Initialization order bugs
  • Memory leaks

2. UndefinedBehaviorSanitizer (UBSan) is a fast undefined behavior detector implemented in Clang and Compiler-rt.

3. ThreadSanitizer (TSan) is a data race detector for C/C++. It finds:
  • Signed integer overflow
  • Out of Bounds Array
  • Out of Bounds BitShifts
  • Floating Point Conversion Overflow
  • Dereferencing Misaligned or Null Pointers
  • Data Races
4. MemorySanitizer (MSan) is a detector of uninitialized memory reads in C/C++ programs.

Good Fuzz Testing Tools

If you are new to fuzzing, we recommend starting with an AI-automated fuzz testing solutions like CI Fuzz by Code Intelligence. It automates many manual steps for you, so that you just need to launch one command to start fuzzing your code.

If you are an advanced user and want to customize a solution for you, you may check out the  list of popular open-source fuzzers: 

  • AFL++ is a C/C++ fuzzer that employs genetic algorithms to increase the code coverage of the test cases efficiently.
  • Honggfuzz is a feedback-driven, easy-to-use fuzzer with interesting analysis options.
  • BFuzz is an input-based fuzzer tool that takes .html as input.
  • Jazzer is a coverage-guided fuzzer for Java and other JVM-based languages.
  • Jazzer.js is a coverage-guided fuzzer for JavaScript and the Node.js platform.
  • KernelFuzzer is a Kernel Fuzzer, for fuzzing Windows.
  • Syzkaller is an unsupervised coverage-guided kernel fuzzer.
  • libFuzzer is a library for coverage-guided fuzz testing and is integrated into the clang/LLVM toolchain.
  • Radamsa is a general-purpose fuzzer.
Fuzz testing solutions comparison guide

You can compare CI Fuzz by Code Intelligence with open-source fuzzers and black-box fuzzing tools in the free guide, 'Fuzz Testing Solutions Comparison.'

Setting Up Fuzzing for Your Project

Integrating white-box fuzz testing into your project is a bit complex, especially finding the right functions to fuzz, but it's a powerful way to improve software quality. 

Here's a breakdown of the essential steps:

  1. Choosing a fuzzing engine: Select a fuzzing tool that fits your project's language and build system. There are many good options available, and starting with a popular one is often a good strategy.
  2. Preparing Your Code: You'll need to make your project compatible with the chosen fuzzer. This might involve adjustments to how your project builds. For example, it is necessary that the used compiler supports the needed instrumentation used by the fuzzer.
  3. Finding Candidates/Targets: To unleash the full potential of fuzzing, it is important to select the right entry points into your code base. Which functions cover the most functionality containing the most complexity?
  4. Creating Fuzz Tests: Write the actual fuzz test that calls the selected function in the correct context, including the needed setup and cleanup. This includes the needed test setup as well as transforming the fuzzer input into usable data structures
  5. Configuring the Fuzzer: Set up the fuzzer with your test program and any specific settings needed. This tells the fuzzer what to test and how to run.
  6. Running the Fuzzer: Let the fuzzer run for a while, ideally as part of your continuous integration (CI) system. It will try many different inputs to find potential issues.
  7. Analyzing Results: When the fuzzer finds something interesting (like a crash), investigate the issue. The fuzzer usually provides helpful information to track down the problem.
  8. Integrating into Your Workflow: For the best results, make fuzzing a regular part of your development process. This ensures that new code is tested and potential issues are found early.

While setting up fuzzing involves several steps, it's a worthwhile investment. Especially now, when technologies can automate some of these steps. For example, Spark, an AI Test Agent by Code Intelligence, runs steps 3-6 autonomously, so you just need to launch one command to analyze your code and generate and run fuzz tests. 

 

Introducing Spark: The Next Generation of Fuzzing Assistance

By combining static analysis with the latest advances in AI by using large language models (LLMs), Spark can identify bugs and vulnerabilities in unknown code without human interaction. 

Spark is the first AI agent to find a real-world vulnerability by automatically generating and running a test for a widely used open-source software (wolfSSL). You can watch the recording of the live demo of fuzzing the wolfSSL’s library here

With Spark, white-box fuzzing is now as simple as: 

  1. Setting up a testing goal (how much code coverage you want to achieve).
  2. Launching Spark.
  3. Reviewing discovered vulnerabilities.

For example, you can set a target of 75% coverage and launch the spark command. Then, Spark would automatically generate fuzz tests and stop fuzzing once it reaches the goal. This is how it would look like in your terminal: 

Spark demo

When you launch Spark, it operates as follows under the hood:

  1. Analyzes your codebase and identifies the most important functions and APIs to fuzz. Each fuzzing entry point is scored based on its expected impact using four key metrics.
  2. Generates fuzz tests (harnesses) for each function. It extracts the correct context needed to test the target function and large language models (LLMs) to create the fuzz test. To get even better results (like higher coverage), Spark validates and refines the created fuzz tests using AI.
  3. Runs the generated tests to trigger findings and calculate coverage.
  4. Flags findings, providing details such as the exact lines of code where issues occur, stack traces, and triggering inputs to assist in root cause analysis.

How to Start Using AI-Automated Fuzzing

If you’re looking to strengthen software security with minimal manual effort, book a free call with our experts for a tailored demo of CI Fuzz and its AI Test Agent.

To start using AI Test Agent, you’ll need to:

  1. Install CI Fuzz.
  2. Ensure access to LLMs.

During the call, Code Intelligence’s experts will guide you through the setup process, share industry best practices, and provide insights on pricing options. 

Start finding bugs autonomously

Closing Thoughts

Getting started with fuzzing is not always easy, and many open-source fuzzers require a lot of skills and knowledge to set up your first test. But I hope this article and CI Fuzz will make it easier for you to get started with fuzzing, as it’s really one of the most effective (and fun) ways to test C/C++ for memory corruptions and other vulnerabilities.

If you have any questions about this article or CI Fuzz, please feel free to reach out via @jochil