跳转至

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][fuchsia-gn], a meta-build system, to generate .ninja files that explicitly describe how to build the system. [GN targets][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.

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][glossary.component manifest source] (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][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][cpp_fuzzer.gni] 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][rustc_fuzzer.gni] 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][go_fuzzer.gni] 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:

  1. 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"
    }
    

  2. 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][variants] is selected, these templates will build a fuzzer binary by linking the [libFuzzer] compiler runtime against code that provides a [fuzz target][fuzz-target] function.

Otherwise, a fuzzer unit test is built by linking a [test harness][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][fuzzer.gni] bundles fuzzers into a Fuchsia [package][glossary.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][gn-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",
  ]
}

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][known_variants] that end in -fuzzer. Each one is an extension of a [sanitizer][sanitizers] 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][ubsan] to detect behavior that violates the language specification such as signed integer overflow, misaligned pointers, and more.
  • lsan: Use [LeakSanitizer][lsan] 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][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][fx-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.

[glossary.package](../../../glossary/README.md#package

cpp_fuzzer.gni[dictionary]: https://llvm.org/docs/LibFuzzer.html#dictionaries fuchsia-gn[fuzz-target]: https://llvm.org/docs/LibFuzzer.html#fuzz-target fuzzer.gnifx-build /development/build/fx.md#configure-a-build

gn-package[gn-targets]: https://gn.googlesource.com/gn/+/HEAD/docs/language.md#Targets

go_fuzzer.gniknown_variants https://clang.llvm.org/docs/LeakSanitizer.html

rustc_fuzzer.gni[sanitizers]: https://github.com/google/sanitizers/wiki test-harness[ubsan]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html

variants


最后更新: 2022 年 12 月 31 日(Saturday) 21:07 CST