Build a fuzzer

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.

Once you have defined your build rules, you can build fuzzers with fx.

Fuchsia library fuzzer GN template

Each language has a specific fuzzer GN template:

C/C++

The fuchsia_library_fuzzer GN template generates an executable target that compiles and links the fuzz target function with the code under test and the fuzzing engine.

To create build rules for a C or C++ fuzzer, add a fuchsia_library_fuzzer GN target to an appropriate BUILD.gn, such as the one with the corresponding unit test rules.

For example:

import("//build/fuzz.gni")

fuchsia_library_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 the fuzzing engine.

To create build rules for 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"
    }
    

When a fuzzing variant is selected, these templates will build a fuzzer binary by linking the [libFuzzer] compiler runtime with the provided sources, deps or both. This code must provide a fuzz target function.

Otherwise, a fuzzer unit test is built by linking a test harness with the provided code. This test harness calls the fuzz target function with fixed inputs, such as a zero length input. This test ensures the fuzzer can compile and link, even when not building for fuzzing.

Host library fuzzer GN template

You can also build fuzzers that run on your development host using the Fuchsia build system. To build host fuzzers, use the host_library_fuzzer GN template.

For example:

host_library_fuzzer("my_host_fuzzer") {
  sources = [ ... ]
  deps = [ ... ]
}

Host fuzzers can be built using fx without adding them to a Fuchsia component or package.

Fuchsia fuzzer component GN template

The fuchsia_fuzzer_component template creates a component used to run the fuzzer. It can include the usual component parameters, such as component_name and deps.

For example:

fuchsia_fuzzer_component("my-fuzzer-component") {
  component_name = "my-fuzzer"
  manifest = "meta/my-fuzzer.cml"
  deps = [ ":my-corpus"]
}

The manifest for library fuzzers must include the default shard for libfuzzer. The output name of the fuzzer must be provided as the first program argument as a package-relative path. Additional arguments may include libFuzzer options, or package-relative paths to directories of seed inputs known as seed corpora.

For example:

{
    include: [
        "//sdk/lib/inspect/client.shard.cml",
        "//src/sys/fuzzing/libfuzzer/default.shard.cml",
        "//src/sys/test_runners/fuzz/default.shard.cml",
        "//src/sys/test_runners/tmp_storage.shard.cml",
        "syslog/client.shard.cml",
    ]
    program: {
        args: [
            "test/my-fuzzer",
            "-max_input_size=256",
            "data/my-corpus",
        ]
    }
}

A seed corpus should match a resource target that is included in the component's deps.

For example:


import("//build/dist/resource.gni")

resource("my-corpus") {
  sources = [
    "input0",
    "input1",
    "input2",
  ]
  outputs = [ "data/my-corpus/{{source_file_part}}" ]
}

Fuchsia fuzzer package GN template

The fuchsia_fuzzer_package template bundles fuzzer components into a Fuchsia package , similar to how fuchsia_test_package bundles test components. The fuchsia_fuzzer_package template is distinguished by adding a specific build rule to annotate fuzzers when built by a fuzzing toolchain variant.

The template includes parameters that are lists of fuzzer components, organized by language. Each language has a set of supported sanitizers provided by their toolchain as compiler runtimes. When the selected toolchain variant includes a sanitizer that is supported for a given language, the corresponding list of fuzzer components are capable of fuzzing.

For example, if the C++ toolchain has support for a hypotheical examplesan, the Rust toolchain does not, and the examplesan-fuzzer variant is selected, then the package definition below builds my-cpp-fuzzer for fuzzing and my-rust-fuzzer for testing only.

fuchsia_fuzzer_package("my-fuzzers") {
  cpp_fuzz_components = [ ":my-cpp-fuzzer" ]
  rust_fuzz_components = [ ":my-rust-fuzzer" ]
}

It is not necessary to include a list if the package has no fuzzers written in the corresponding languages.

A fuchsia_fuzzer_package can use all the same parameters as a fuchsia_package.

For example:

fuchsia_fuzzer_package("my-fuzzers") {
  package_name = "the-fuzzers"
  cpp_fuzz_components = [ ":my-fuzzer" ]
}

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

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 fuzzer_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

After running fx set, you can view the currently configured fuzzers with ffx fuzz list. Additional ffx fuzz commands can be used to run a fuzzer.