This guide assumes you have already created a fuzzer that you now want to build. It uses the same sample code as in that guide.
Fuchsia uses GN, a meta-build system, to generate .ninja
files that explicitly describe how to build the system. GN targets are nodes in the build graph that represent a specific output such as a library or executable. GN templates are rules that generate additional targets.
In order to make adding new fuzzers as easy as possible, Fuchsia provides fuzzing-related GN templates.
Each language has a specific fuzzer GN template. All of these templates support certain common parameters, as detailed in fuzzer.gni:
cmx
file is provided, it is combined with and overrides the generated file.dictionary
. If not provided, an empty dictionary file is created.options
]. These key-value pairs are written to a options file.For example:
cpp_fuzzer("my-fuzzer") { output_name = "the-fuzzer" sources = [ "my_fuzzer.cc" ] deps = [ ":my-lib" ] dictionary = "my.dict" cmx = "meta/the-fuzzer.cmx" options = [ "key1=val1", "key2=val2", ] }
Each language has a specific fuzzer GN template:
{C/C++} The cpp_fuzzer
GN template generates a GN target that compiles the fuzz target function and links it with the code under test and with libFuzzer.
To build a C or C++ fuzzer, add a cpp_fuzzer
GN target to an appropriate BUILD.gn.
For example:
import("//build/cpp/cpp_fuzzer.gni") cpp_fuzzer("parser-fuzzer") { sources = [ "parser_fuzzer.cc" ] deps = [ ":parser-lib" ] }
{Rust}
The rustc_fuzzer
GN template generates a GN target that compiles the Rust fuzz target function into a C object file that it then links with libFuzzer.
To build a Rust fuzzer, add a rustc_fuzzer
GN target to the crate's BUILD.gn.
When choosing where and how to add this target, consider the following:
It is recommended to have the fuzzer name match the fuzz target function name, and to include the fuzz target function in a Rust library, i.e. in src/lib.rs
. You may leave the body of the template empty when following these recommendations. For example, using the toy_example_arbitrary
example, you would add the following to your BUILD.gn
:
import("//build/rust/rustc_fuzzer.gni") rustc_fuzzer("toy_example_arbitrary") { }
If the fuzz target function name differs from the fuzzer name, you must provide it with the rustfunction
parameter. For example, using the toy_example_u8
example, you would add the following to your BUILD.gn
:
import("//build/rust/rustc_fuzzer.gni") rustc_fuzzer("toy_example_raw_bytes") { rustfunction = "toy_example_u8" }
If the code to be tested cannot be easily factored into a library, a Rust binary can be used with two additional steps:
You must exclude the main
function from compilation, along with any items not used when fuzzing, e.g. imports only used in main
. For example:
#[cfg(not(fuzz))] use only::used::in::main; #[cfg(not(fuzz))] fn main() { ... }
You must explicitly provide the fuzz target function to the rustc_fuzzer
with the source_root
parameter. For example, in your BUILD.gn
:
import("//build/rust/rustc_fuzzer.gni") rustc_fuzzer("toy_example_with_main") { source_root = "src/main.rs" }
{Go}
The go_fuzzer
GN template generates a GN target that compiles the Go fuzz target function into a C object file that it then links with libFuzzer.
To build a Go fuzzer:
Ensure the Go package in the previous step is available as a go_library
GN target.
For example:
import("//build/go/go_library.gni") go_library("my_library") { sources = "pkg/file_with_fuzz.go" }
Write a go_fuzzer
GN target to build the package containing the fuzz target function. Make sure to include the go_library
in deps
.
For example:
import("//build/go/go_fuzzer.gni") go_fuzzer("my_fuzzer") { gopackage = "my_library/pkg" deps = [ ":my_library" ] }
When a fuzzing variant is selected, these templates will build a fuzzer binary by linking the [libFuzzer] compiler runtime against code that provides a fuzz target function.
Otherwise, a fuzzer unit test is built by linking a test harness that calls the fuzz target function with a zero length input against the provided sources
, deps
, or both. This test ensures the fuzzer can compile and link, even when not building for fuzzing.
Note: Since the generated unit test uses a zero-length input, your fuzzer must not crash when provided with a zero-length input. If a fuzzer input is shorter than your fuzzer's minimum input length, you can simply return early.
The fuzzers_package
template bundles fuzzers into a Fuchsia package similar to how a normal package
bundles binaries or a test_package
bundles tests. The fuzzers_package
template is distinguished from these other package templates in how it interacts with the currently selected toolchain variants.
Note: The Fuchsia build system will build the fuzzers only if their package is selected by a fuzzing variant. See Build fuzzers with fx.
The most important parameters to the template are the lists of fuzzers, organized by language.
For example:
fuzzers_package("my-fuzzers") { cpp_fuzzers = [ ":my-cpp-fuzzer" ] go_fuzzers = [ ":my-go-fuzzer" ] rust_fuzzers = [ ":my-rust-fuzzer" ] }
It is not necessary to include a list if the package has no fuzzers written in the corresponding languages.
A fuzzers_package
can use all the same parameters as a fuchsia_package
.
For example:
fuzzers_package("my-fuzzers") { package_name = "the-fuzzers" cpp_fuzzers = [ ":my-fuzzer" ] }
Additional parameters include:
fuzz_host
: Also builds a fuzzer as a host tool (when selected). Defaults to false.host_only
: Implies fuzz_host
and does not create a Fuchsia package. Defaults to false.sanitizers
: Sets the sanitizers to match during selection. Defaults to language-specific lists in fuzzer.gni. This typically does not need to be set.For example:
fuzzers_package("my-fuzzers") { cpp_fuzzers = [ ":my-fuzzer" ] fuzz_host = true }
The list of fuzzers can contain a mix of GN labels and scopes. Each scope element must include a label and can override the parameters above. Additionally, scopes can indicate output names for fuzzers that specify them.
For example:
fuzzers_package("my-fuzzers") { cpp_fuzzers = [ { label = ":my-fuzzer" output_name = "the-fuzzer" }, { label = ":no-host-fuzzer" fuzz_host = false }, ] fuzz_host = true }
Once defined, a package needs to be included in the build dependency graph like any other test package. This typically means adding it to a group of tests.
For example:
group("tests") { deps = [ ":my-test-package", ":my-fuzzers", ] }
fx
As noted above, the Fuchsia build system will build the fuzzers only if it is explicitly told to instrument them for fuzzing with an appropriate fuzzing variant. These are the known variants that end in -fuzzer
. Each one is an extension of a sanitizer variant, including:
The easiest way to build a fuzzers_package
with a fuzzing variant is to use the --fuzz-with <sanitizer>
flag with fx set
.
For example:
Note: In some situations, Ninja cannot determine when an output needs to be rebuilt as a result of compiler configuration changes. If building fails, try fx clean-build
.
After running fx set
, you can view the currently configured fuzzers with fx fuzz list
. Additional fx fuzz
commands can be used to run a fuzzer.