Build a fuzzer
July 8, 2022 ยท View on GitHub
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.
Fuzzer GN template {#fuzzer}
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
cmxfile 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_fuzzerGN 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_fuzzerGN 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_fuzzerGN 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_fuzzerGN 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_arbitraryexample, 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
rustfunctionparameter. For example, using thetoy_example_u8example, 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
mainfunction 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_fuzzerwith thesource_rootparameter. 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_fuzzerGN 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_libraryGN target.For example:
import("//build/go/go_library.gni") go_library("my_library") { sources = "pkg/file_with_fuzz.go" } -
Write a
go_fuzzerGN target to build the package containing the fuzz target function. Make sure to include thego_libraryindeps.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.
Fuzzers package GN template {#fuzzers-package}
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_hostand 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",
]
}
Build fuzzers with fx {#fx-set}
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:testsfx 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.