Skip to main content

Create a fuzz test

This section describes how to create a fuzz test using CI Fuzz. It covers the cifuzz create command and provides details needed to integrate cifuzz with specific build systems.


Overview

The cifuzz create command creates a fuzz test template you can use to start writing a fuzz test. Depending on the language and build system you are using, you may also need to modify some build system configuration files. These are detailed below.

C/C++

CMake

Run the following cifuzz command:

cifuzz create cpp -o <path to fuzz test>

This will create a fuzz test template in the location you specify. We recommend creating your fuzz tests close to the code being tested, just as you would for a unit test.

After creating your fuzz test, you need to add the add_fuzz_test and target_link_libraries commands to your CMakeLists.txt file so CMake can find, build, and link your fuzz test. Add the following to your CMakeLists.txt (with names and paths modified to match your project):

add_fuzz_test(my_fuzz_test test/my_fuzz_test.cpp)
target_link_libraries(my_fuzz_test PRIVATE exploreMe)

You can see an example CMakeLists.txt in the cifuzz repo: example CMakeLists.txt file

Bazel

Run the following cifuzz command:

cifuzz create cpp -o <path to fuzz test>

This will create a fuzz test template in the location you specify. We recommend creating your fuzz tests close to the code being tested, just as you would for a unit test.

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

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

cc_fuzz_test(
name = "my_fuzz_test",
srcs = ["my_fuzz_test.cpp"],
corpus = glob(
["my_fuzz_test_inputs/**"],
allow_empty = True,
),
deps = [
"//src:explore_me",
"@cifuzz"
],
)

You can see an example BUILD.bazel in the cifuzz repo: example BUILD.bazel file

Make

While cifuzz provides direct support for C/C++ CMake and Bazel projects, it can generally support several other build systems by allowing you to configure build-commands that enable your project to build the fuzz tests properly. Here is an example for Make.

Run the following cifuzz command:

cifuzz create cpp -o <path to fuzz test>

This will create a fuzz test template in the location you specify. We recommend creating your fuzz tests close to the code being tested, just as you would for a unit test.

Next, you need to create a target in the Makefile for the fuzz test. To enable building the target as a fuzz test, cifuzz 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, CXX, CFLAGS, CXXFLAGS variables should be used for both the fuzz test and the software under test. Here are the targets for the software under test (libexplore.so) and the fuzz test from the example project Makefile:

libexplore.so: src/explore_me.cpp src/explore_me.h
${CXX} ${CXXFLAGS} -shared -fpic -o libexplore.so $<
my_fuzz_test: libexplore.so
${CXX} ${CXXFLAGS} ${FUZZ_TEST_CFLAGS} ${FUZZ_TEST_LDFLAGS} -o $@ $@.cpp -Wl,-rpath '-Wl,$$ORIGIN' -L. -lexplore

Please make sure you don't overwrite CFLAGS and CXXFLAGS in your Makefile or included files. CI Fuzz relies on being able to add flags to CFLAGS and CXXFLAGS variables to instrument software under test. If you set these variables to a hardcoded value, you will remove instrumentation. You can check if they are being overwritten for example with this command:

grep -Ri -e CFLAGS*[^+]= -e CXXFLAGS*[^+]=

You can then adjust your Makefile to preserve options set by CI Fuzz:

CXXFLAGS += <your options>

You can see an example Makefile in the cifuzz repo: example Makefile

Meson

While cifuzz provides direct support for C/C++ CMake and Bazel projects, it can generally support several other build systems by allowing you to configure build-commands that enable your project to build the fuzz tests properly. Here is an example for Meson.

Run the following cifuzz command:

cifuzz create cpp -o <path to fuzz test>

This will create a fuzz test template in the location you specify. We recommend creating your fuzz tests close to the code being tested, just as you would for a unit test.

To enable building the target as a fuzz test, cifuzz 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, CXX, CFLAGS, CXXFLAGS variables should be used for both the fuzz test and the software under test.

For Meson, you need to:

  • make the cifuzz ENV variables accessible by creating meson variables in meson.build.
  • add global compiler and linker arguments
  • link the fuzz test you create with the library you want to fuzz

Examples for each of the above points:

# make cifuzz ENV variables accessible as meson variables
cmd = run_command('sh', '-c', 'echo $FUZZ_TEST_CFLAGS')
FUZZ_TEST_CFLAGS = cmd.stdout().strip().strip('\'')
cmd = run_command('sh', '-c', 'echo $FUZZ_TEST_LDFLAGS')
FUZZ_TEST_LDFLAGS = cmd.stdout().strip().strip('\'')
cmd = run_command('sh', '-c', 'echo $CFLAGS')
CFLAGS = cmd.stdout().strip().strip('\'').split(' ')
cmd = run_command('sh', '-c', 'echo $LDFLAGS')
LDFLAGS = cmd.stdout().strip().strip('\'').split(' ')

# add global compiler and linker arguments
add_global_arguments(CFLAGS, language: 'cpp')
add_global_link_arguments(LDFLAGS, language: 'cpp')

# link the fuzz test you created with the target library
executable('my_fuzz_test', 'my_fuzz_test.cpp',
link_with: [explore_me_shared_lib],
cpp_args: [FUZZ_TEST_CFLAGS],
link_args: FUZZ_TEST_LDFLAGS
)

Please make sure you don't overwrite these variables later on, as that would remove flags added by CI Fuzz.

You can see a complete meson.build example (and project) in this repo: example meson.build file

Java


Maven

To create the fuzz test template for a Java Maven project, you just need to run the following command:

cifuzz create java -o <path to fuzz test>

This will create a fuzz test template in the location you specify. We recommend creating your fuzz tests close to the code being tested, just as you would for a unit test.

Fuzz tests for Maven projects do not require any further configuration of Maven itself.

You can see an example fuzz test for a Maven project in the cifuzz repo: example Maven fuzz test

Gradle

To create the fuzz test template for a Java Gradle project, you just need to run the following command:

cifuzz create java -o <path to fuzz test>

This will create a fuzz test template in the location you specify. We recommend creating your fuzz tests close to the code being tested, just as you would for a unit test.

Fuzz tests for Gradle projects do not require any further configuration of Gradle itself.

You can see an example fuzz test for a Gradle project in the cifuzz repo: example Gradle fuzz test