This repository contains Bazel Starlark extensions for defining fuzz tests in Bazel projects.
Fuzzing is an effective technique for uncovering security and stability bugs in software. Fuzzing works by invoking the code under test (e.g., a library API) with automatically generated data, and observing its execution to discover incorrect behavior, such as memory corruption or failed invariants. Read more here about fuzzing, additional examples, best practices, and other resources.
The rule library currently provides support for C++ fuzz tests. Support for additional languages may be added in the future.
Contributions are welcome! Please read the contribution guidelines.
This section will walk you through the steps to set up fuzzing in your Bazel project and write your first fuzz test. We assume Bazel is installed on your machine.
The fuzz tests require a Clang compiler. The libFuzzer engine requires at least Clang 6.0. In addition, the Honggfuzz engine requires the libunwind-dev
and libblocksruntime-dev
packages:
$ sudo apt-get install clang libunwind-dev libblocksruntime-dev
Add the following to your WORKSPACE
file:
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_fuzzing", sha256 = "94f25c7a18db0502ace26a3ef7d0a25fd7c195c4e9770ddd1b1ec718e8936091", strip_prefix = "rules_fuzzing-0.1.3", urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.1.3.zip"], ) load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies") rules_fuzzing_dependencies() load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init") rules_fuzzing_init()
NOTE: Replace this snippet with the latest WORKSPACE setup instructions in the release notes. To get the latest unreleased features, you may need to change the
urls
andsha256
attributes to fetch fromHEAD
.
It is best to create command shorthands for the fuzzing configurations you will use during development. In our case, let's create a configuration for libFuzzer + Address Sanitizer. In your .bazelrc
file, add the following:
# Force the use of Clang for C++ builds. build --action_env=CC=clang build --action_env=CXX=clang++ # Define the --config=asan-libfuzzer configuration. build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
A fuzz test is specified using a cc_fuzz_test
rule. In the most basic form, a fuzz test requires a source file that implements the fuzz driver entry point.
Let's create a fuzz test that exhibits a buffer overflow. Create a fuzz_test.cc
file in your workspace root, as follows:
#include <cstddef> #include <cstdint> #include <cstdio> void TriggerBufferOverflow(const uint8_t *data, size_t size) { if (size >= 3 && data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[size] == 'Z') { fprintf(stderr, "BUFFER OVERFLOW!\n"); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { TriggerBufferOverflow(data, size); return 0; }
Let's now define its build target in the BUILD
file:
load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") cc_fuzz_test( name = "fuzz_test", srcs = ["fuzz_test.cc"], )
You can now build and run the fuzz test. For each fuzz test <name>
defined, the framework automatically generates a launcher tool <name>_run
that will build and run the fuzz test according to the configuration specified:
$ bazel run --config=asan-libfuzzer //:fuzz_test_run
Our libFuzzer test will start running and immediately discover the buffer overflow issue in the code:
INFO: Seed: 2957541205 INFO: Loaded 1 modules (8 inline 8-bit counters): 8 [0x5aab10, 0x5aab18), INFO: Loaded 1 PC tables (8 PCs): 8 [0x5aab18,0x5aab98), INFO: 755 files found in /tmp/fuzzing/corpus INFO: 0 files found in fuzz_test_corpus INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 35982 bytes INFO: seed corpus: files: 755 min: 1b max: 35982b total: 252654b rss: 35Mb #756 INITED cov: 6 ft: 7 corp: 4/10b exec/s: 0 rss: 47Mb ================================================================= ==724294==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000047a74 at pc 0x0000005512d9 bp 0x7fff3049d270 sp 0x7fff3049d268
The crash is saved under /tmp/fuzzing/artifacts
and can be further inspected.
Once you wrote and tested the fuzz test, you should run it on continuous fuzzing infrastructure so it starts generating tests and finding new crashes in your code.
The fuzzing rules provide out-of-the-box support for OSS-Fuzz, free continuous fuzzing infrastructure from Google for open source projects. Read its Bazel project guide for detailed instructions.
Congratulations, you have built and run your first fuzz test with the Bazel rules!
Check out the examples/
directory, which showcases additional features. Read the User Guide for detailed usage instructions.