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.
- To build a fuzzer binary, see Fuzzer GN template for the appropriate language.
- To assemble a package of fuzzer binaries, see Fuzzers package GN template.
Each language has a specific fuzzer GN template. All of these templates support certain common parameters, as detailed in fuzzer.gni:
- An optional component manifest (cmx) file. A manifest for fuzzing is always generated. If a
cmx
file is provided, it is combined with and overrides the generated file. - An optional
dictionary
. If not provided, an empty dictionary file is created. - An optional list of libFuzzer [
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 thetoy_example_arbitrary
example, you would add the following to yourBUILD.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 thetoy_example_u8
example, you would add the following to yourBUILD.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 inmain
. 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 thesource_root
parameter. For example, in yourBUILD.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 thego_library
indeps
.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
: Impliesfuzz_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",
]
}
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:
- asan: Use AddressSanitizer to detect memory errors such as memory usage after free or return, heap and stack buffer overflows, and more.
- ubsan: Use UndefinedBehaviorSanitizer to detect behavior that violates the language specification such as signed integer overflow, misaligned pointers, and more.
- lsan: Use LeakSanitizer to detect memory leaks.
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:
fx set core.x64 --fuzz-with asan --with //bundles:tests
fx build
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.