blob: e7f4a70951b7fea04fe8d3041617fecbef8523d6 [file] [log] [blame] [view]
# Quickstart with CMake
This tutorial describes how to set up your development environment with CMake as
the build system, and how to get a simple fuzz test up and running. We recommend
this tutorial if you're new to FuzzTest or if you need a quick refresher.
## Definitions
The library can work in several different modes.
* Unit test mode (*default*): FUZZ_TESTs are built without coverage
instrumentation and run with random inputs for short period of time along
with regular unit TESTs.
* Fuzzing mode (controlled by `-DFUZZTEST_FUZZING_MODE=on`): FUZZ_TESTs are
built with coverage instrumentation and run individually and indefinitely or
for specified amount of time.
* Compatibility mode (controlled by
`-DFUZZTEST_COMPATIBILITY_MODE=libfuzzer`): like Fuzzing mode, but an
external engine is used instead FuzzTest's built in engine. Currently
[libFuzzer](https://llvm.org/docs/LibFuzzer.html) is supported.
**Warning:** compatibility mode is experimental and does not guarantee full
FuzzTest features.
## Overall rules
We provide a default CMake option for unittest mode, the instructions are
straightforward and work like
[googletest](https://google.github.io/googletest/quickstart-cmake.html).
To run in a fuzztest mode, you need to make sure all targets are compiled in a
special way.
* `-g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -UNDEBUG`
* `-fsanitize-coverage=inline-8bit-counters -fsanitize-coverage=trace-cmp`
* `-fsanitize=address -DADDRESS_SANITIZER` (optionally enabling sanitizer)
The FuzzTest framework doesn't need to be built with these flags, only the target project.
For all projects to have correct flags set, use a CMake macro
`fuzztest_setup_fuzzing_flags()`.
For a concrete example, see the root CMakeLists.txt. First, we build framework
without additional flags and then build the rest of translation units with
coverage flags. Otherwise we might get into recursive issues, i.e. fuzztest
framework will be fuzzing itself.
Easiest way to make sure you build everything correctly is to invoke
`add_subdirectory` first in your project on fuzztest.
## Prerequisites
To use FuzzTest, you'll need:
* A Linux-based operating system.
* [Clang](https://clang.llvm.org/). GCC is not yet supported. Unittest mode
mode should work but it's not yet tested.
* [CMake](https://https://cmake.org/).
## Set up a CMake workspace
First, create a directory where your project will reside:
```sh
$ mkdir first_fuzz_project && cd first_fuzz_project
$ git clone https://github.com/google/fuzztest.git
```
Create a `CMakeLists.txt`.
```
cmake_minimum_required(VERSION 3.19)
project(first_fuzz_project)
# GoogleTest requires at least C++17
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(fuzztest)
```
FuzzTest CMake already handles its dependencies on its own:
[RE2](https://github.com/google/re2),
[Abseil](https://github.com/abseil/abseil-cpp). We highly recommend using
[GoogleTest](https://github.com/google/googletest) as well.
## Create and run a fuzz test
With the CMake workspace set up, you can start using FuzzTest. Let's create a
trivial example to make sure everything runs correctly.
Create a file named `first_fuzz_test.cc` in the directory `first_fuzz` with the
following contents:
```c++
#include "fuzztest/fuzztest.h"
#include "gtest/gtest.h"
TEST(MyTestSuite, OnePlustTwoIsTwoPlusOne) {
EXPECT_EQ(1 + 2, 2 + 1);
}
void IntegerAdditionCommutes(int a, int b) {
EXPECT_EQ(a + b, b + a);
}
FUZZ_TEST(MyTestSuite, IntegerAdditionCommutes);
```
The file contains two tests in the test suite named `MyTestSuite`. The first one
is a unit test named `OnePlustTwoIsTwoPlusOne` asserting that integer addition
commutes for two specific values.
The second test is a fuzz test that captures the commutative property of integer
addition more generally. The test consists of a property function with a
suggestive name `IntegerAdditionCommutes`, and the test registration using the
macro `FUZZ_TEST`. The property function asserts the commutative property for
two generic integer parameters.
To build and run the tests, **add** to the file `CMakeLists.txt` the following
content:
```cmake
...
enable_testing()
include(GoogleTest)
add_executable(
first_fuzz_test
first_fuzz_test.cc
)
# If you have other dependencies than FuzzTest, link them:
# target_link_libraries(first_fuzz_test PRIVATE other_deps)
link_fuzztest(first_fuzz_test)
gtest_discover_tests(first_fuzz_test)
```
This defines a C++ test binary and links it with the FuzzTest and GoogleTest
libraries, as well as the version of the default GoogleTest main that supports
FuzzTest.
### Unit test mode
There are several ways to run the tests: in the unit test mode and in the
fuzzing mode (see above). The unit test mode runs both test for a short time
without sanitizer and coverage instrumentation:
```
$ mkdir build && cd build
$ CC=clang CXX=clang++ cmake \
-DCMAKE_BUILD_TYPE=RelWithDebug ..
$ cmake --build .
$ ./first_fuzz_test
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from MyTestSuite
[ RUN ] MyTestSuite.OnePlustTwoIsTwoPlusOne
[ OK ] MyTestSuite.OnePlustTwoIsTwoPlusOne (0 ms)
[ RUN ] MyTestSuite.IntegerAdditionCommutes
[ OK ] MyTestSuite.IntegerAdditionCommutes (13 ms)
[----------] 2 tests from MyTestSuite (13 ms total)
[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (13 ms total)
[ PASSED ] 2 tests.
```
### Fuzzing mode
The fuzzing mode runs a single specified fuzz test with sanitizer and coverage
instrumentation. It keeps running the test with different input values until it
finds a crash or it is explicitly terminated by the user.
Before `add_executable(first_fuzz_test ...)`, add the specified flags:
```cmake
fuzztest_setup_fuzzing_flags()
```
Then run:
```
$ CC=clang CXX=clang++ cmake \
-DCMAKE_BUILD_TYPE=RelWithDebug \
-DFUZZTEST_FUZZING_MODE=on ..
$ cmake --build .
$ ./first_fuzz_test --fuzz=MyTestSuite.IntegerAdditionCommutes
[.] Sanitizer coverage enabled. Counter map size: 21290, Cmp map size: 262144
Note: Google Test filter = MyTestSuite.IntegerAdditionCommutes
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MyTestSuite
[ RUN ] MyTestSuite.IntegerAdditionCommutes
[*] Corpus size: 1 | Edges covered: 131 | Fuzzing time: 504.798us | Total runs: 1.00e+00 | Runs/secs: 1
[*] Corpus size: 2 | Edges covered: 133 | Fuzzing time: 934.176us | Total runs: 3.00e+00 | Runs/secs: 3
[*] Corpus size: 3 | Edges covered: 134 | Fuzzing time: 2.384383ms | Total runs: 5.30e+01 | Runs/secs: 53
[*] Corpus size: 4 | Edges covered: 137 | Fuzzing time: 2.732274ms | Total runs: 5.40e+01 | Runs/secs: 54
[*] Corpus size: 5 | Edges covered: 137 | Fuzzing time: 7.275553ms | Total runs: 2.48e+02 | Runs/secs: 248
[*] Corpus size: 6 | Edges covered: 137 | Fuzzing time: 21.97155ms | Total runs: 9.12e+02 | Runs/secs: 912
[*] Corpus size: 7 | Edges covered: 137 | Fuzzing time: 166.721522ms | Total runs: 8.49e+03 | Runs/secs: 8491
[*] Corpus size: 8 | Edges covered: 146 | Fuzzing time: 500.398497ms | Total runs: 2.77e+04 | Runs/secs: 27741
[*] Corpus size: 9 | Edges covered: 146 | Fuzzing time: 500.821386ms | Total runs: 2.77e+04 | Runs/secs: 27742
[*] Corpus size: 10 | Edges covered: 147 | Fuzzing time: 2.597553524s | Total runs: 1.75e+05 | Runs/secs: 87476
^C
```
Congratulations! You're now all set for fuzzing with FuzzTest.
### Compatibility mode
**Warning:** compatibility mode is experimental and does not guarantee full
FuzzTest features.
```
$ CC=clang CXX=clang++ cmake \
-DCMAKE_BUILD_TYPE=RelWithDebug \
-DFUZZTEST_COMPATIBILITY_MODE=libfuzzer ..
$ cmake --build .
$ ./first_fuzz_test --fuzz=MyTestSuite.IntegerAdditionCommutes
Note: Google Test filter = MyTestSuite.IntegerAdditionCommutes
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MyTestSuite
[ RUN ] MyTestSuite.IntegerAdditionCommutes
FUZZTEST_PRNG_SEED=OffQXb8u5_vZtH4-7wgVOLu_HNAhPIbLz7CFF13u3nk
INFO: found LLVMFuzzerCustomMutator (0x55edfe61cac0). Disabling -len_control by default.
INFO: libFuzzer ignores flags that start with '--'
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1109364873
INFO: Loaded 1 modules (18692 inline 8-bit counters): 18692 [0x55edfe975548, 0x55edfe979e4c),
INFO: Loaded 1 PC tables (18692 PCs): 18692 [0x55edfe979e50,0x55edfe9c2e90),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 17 ft: 18 corp: 1/1b exec/s: 0 rss: 38Mb
#3 NEW cov: 104 ft: 108 corp: 2/66b lim: 4096 exec/s: 0 rss: 38Mb L: 65/65 MS: 1 Custom-
#4 NEW cov: 105 ft: 112 corp: 3/121b lim: 4096 exec/s: 0 rss: 38Mb L: 55/65 MS: 1 Custom-
#5 NEW cov: 105 ft: 113 corp: 4/196b lim: 4096 exec/s: 0 rss: 38Mb L: 75/75 MS: 1 Custom-
#16 REDUCE cov: 105 ft: 113 corp: 4/195b lim: 4096 exec/s: 0 rss: 38Mb L: 54/75 MS: 1 Custom-
#41 NEW cov: 105 ft: 114 corp: 5/247b lim: 4096 exec/s: 0 rss: 38Mb L: 52/75 MS: 5 Custom-Custom-Custom-Custom-Custom-
#65 NEW cov: 106 ft: 115 corp: 6/312b lim: 4096 exec/s: 0 rss: 38Mb L: 65/75 MS: 4 Custom-Custom-Custom-Custom-
#69 NEW cov: 108 ft: 119 corp: 7/365b lim: 4096 exec/s: 0 rss: 38Mb L: 53/75 MS: 4 Custom-Custom-Custom-Custom-
```
## Next steps
* This tutorial covered the basic setup of the build environment with CMake.
To learn more about the FuzzTest framework and how to use it, check out the
[Codelab](tutorial.md).
* Explore the rest of the [documentation](./).