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. One-time setup required by the FUZZ_TEST function can be configured in FUZZ_TEST_SETUP().

FuzzedDataProvider

While not required, it is 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 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 being tested, just as you would for a unit test.

Integrate a Fuzz Test into your project

Depending on your build system, the following steps are needed 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 that should be used for the Fuzz Test and the software under test
  • CXXFLAGS - Sets the necessary C++ compiler and linker flags that should be used for the Fuzz Test and the software under test
  • FUZZ_TEST_CFLAGS - Sets the necessary compiler flags that should be used for the Fuzz Test
  • FUZZ_TEST_LD_FLAGS - Sets the necessary linker flags that should be used for the Fuzz Test

FUZZ_TEST_CFLAGS and FUZZ_TEST_LDFLAGS are environment variables which must both be passed to the compiler and linker command for the Fuzz Test. The CC and CFLAGS/CXX and CXXFLAGS variables should be used for both the fuzz test and the software under test.

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>