Skip to main content

Write a C/C++ fuzz test

Structure of a fuzz test

my_fuzz_test.cpp
#include "<target>.h"
#include <cifuzz/cifuzz.h>

FUZZ_TEST_SETUP() {
// Optional one-time setup
}

FUZZ_TEST(const uint8_t *data, size_t size) {
// Call target function with data
}

The fuzz test must include the header for the target function and cifuzz itself to be able to run. You can configure one-time setup required by the FUZZ_TEST in FUZZ_TEST_SETUP().

FuzzedDataProvider

While not required, it's recommended to use the FuzzedDataProvider in your fuzz test to split the fuzzing input in the data variable into different data types, which you can use as parameters for the function you want to fuzz.

Example
FUZZ_TEST(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
int a = fuzzed_data.ConsumeIntegral<int>();
targetFunction(a);
}

The C++ implementation of the FuzzedDataProvider is part of the LLVM Project. You can find the documentation in Google's Fuzzing repository and the LLVM repository.

Create a fuzz test template

You can run the following cifuzz command to create a fuzz test template in the specified location:

cifuzz create cpp -o <path to fuzz test>

Create your fuzz tests close to the code you want to test, just as you would for a unit test.

Integrate a fuzz test into your project

Depending on your build system, you need to follow the next steps to successfully integrate the fuzz test into your project.

CMake

After creating your fuzz test, you need to add it with the add_fuzz_test command to your CMakeLists.txt file and link the target library:

CMakeLists.txt
add_fuzz_test(<fuzz test name> <path/to/fuzz_test.cpp>)
target_link_libraries(<fuzz test name> PRIVATE <target>)

Bazel

After creating your fuzz test, you need to define a Bazel target by adding the following to the BUILD.bazel file:

BUILD.bazel
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")

cc_fuzz_test(
name = "<fuzz test name>",
srcs = ["<fuzz test.cpp>"],
corpus = glob(
["<fuzz test name>_inputs/**"],
allow_empty = True,
),
deps = [
"//<target>",
"@cifuzz"
],
)

Make

After creating your fuzz test, you need to create a target in the Makefile for the fuzz test:

Makefile
lib<target>.so: <path/to/target.cpp> <path/to/target.h>
${CXX} ${CXXFLAGS} -shared -fpic -o lib<target>.so $<
<fuzz test name>: <target>.so
${CXX} ${CXXFLAGS} ${FUZZ_TEST_CFLAGS} ${FUZZ_TEST_LDFLAGS} -o $@ $@.cpp -Wl,-rpath '-Wl,$$ORIGIN' -L. -l<target>

When running the fuzz test, CI Fuzz sets the following environment variables:

  • CC - Sets the C compiler to clang
  • CXX - Sets the C++ compiler to clang++
  • CFLAGS - Sets the necessary C compiler and linker flags to use for the fuzz test and the SUT
  • CXXFLAGS - Sets the necessary C++ compiler and linker flags to use for the fuzz test and the SUT
  • FUZZ_TEST_CFLAGS - Sets the necessary compiler flags to use for the fuzz test
  • FUZZ_TEST_LD_FLAGS - Sets the necessary linker flags to use for the fuzz test

You must pass the environment variables FUZZ_TEST_CFLAGS and FUZZ_TEST_LDFLAGS to the compiler and linker command for the fuzz test. Use the CC and CFLAGS/CXX and CXXFLAGS for both the fuzz test and the SUT.

Don't overwrite CFLAGS and CXXFLAGS in your Makefile or included files, because CI Fuzz uses them for instrumentation. If you need to set them, be sure to add your options instead:

CXXFLAGS += <your options>