| # Write a fuzzer | |
| Fuchsia's toolchain supports fuzzing using LLVM's [libFuzzer]. To create a fuzzer for a particular | |
| interface, you need to implement a [fuzz target function][fuzz-target] that uses a provided sequence | |
| of bytes to exercise the interface. The sequence of bytes is referred to as a fuzzer "input". The | |
| fuzz target function is used by libFuzzer to search for inputs that cause panics or other errors. | |
| ## Sample Code to Fuzz {#samples} | |
| For each of the examples below, assume you want to test code like the following: | |
| * {C/C++} | |
| ```cpp | |
| class Parser { | |
| Parser(const std::string &name, uint32_t flags); | |
| virtual ~Parser(); | |
| int Parse(const uint8 *buf, size_t len); | |
| }; | |
| ``` | |
| * {Rust} | |
| ```rust | |
| struct ToyStruct { | |
| n: u8, | |
| s: String, | |
| } | |
| fn toy_example(input: ToyStruct) -> Result<u8, &'static str>; | |
| ``` | |
| * {Go} | |
| ```golang | |
| package mypackage | |
| func HandleBytes(s []byte) { | |
| // Complicated code goes here | |
| } | |
| type MyStruct struct { | |
| // Various fields | |
| } | |
| func HandleStruct(s MyStruct) { | |
| // Complicated code goes here | |
| } | |
| ``` | |
| ## Simple Fuzz Target Function {#basic} | |
| For each language, your fuzz target function will use the bytes provided to call the code you want | |
| to fuzz. If the interface being fuzzed has documented constraints on its parameters, you can reject | |
| inputs that don't meet those constraints. You can also ignore returned errors since failing | |
| gracefully on invalid parameters is correct behavior. | |
| * {C/C++} | |
| For C and C++, the fuzz target function must be have the signatue | |
| `extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)` and return 0: | |
| ```cpp | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
| Parser parser("", 0); | |
| if (size < 5) { | |
| return 0; | |
| } | |
| parser.Parse(data, size); | |
| return 0; | |
| } | |
| ``` | |
| Place this code in a source file adjacent to code being fuzzed as you would with a unit test. For | |
| example, the code above might be in `parser-fuzztest.cc`. | |
| * {Rust} | |
| For Rust, the recommended approach is to use the `Arbitrary` trait discussed in the next section. | |
| It is also possible to create a "manual" fuzz target function that is analgous to the simple fuzz | |
| target functions in other languages. This function must take a reference to a byte slice as its | |
| single parameter, return nothing, and have the [`#[fuzz]`][fuzz-crate] attribute: | |
| ```rust | |
| use fuzz::fuzz; | |
| #[fuzz] | |
| fn toy_example_u8(input: &[u8]) { | |
| if input.len() == 0 { | |
| return | |
| } | |
| let n = input[0]; | |
| if let Ok(s) = std::str::from_utf8(input[1:]) { | |
| let _ = toy_example(ToyStruct{n, s: s.to_string(),}); | |
| } | |
| } | |
| ``` | |
| As with unit tests, this code can be place in the same file as the code it is testing. For | |
| example, the code above might be in `toy_example/src/lib.rs`. | |
| * {Go} | |
| For Go the fuzz target function must have the signature `func Fuzz(s []byte)` and return nothing. | |
| ```golang | |
| func Fuzz(s []byte) { | |
| mypackage.HandleBytes(s) | |
| } | |
| ``` | |
| This function can be added to and exported from either an existing Go package, or a new Go package | |
| if no existing package is a good fit. | |
| ## Support for Fuzzing More Complex Types {#advanced} | |
| Each language has utilities to facilitate making more complicated fuzz target functions: | |
| * {C/C++} | |
| The [`FuzzedDataProvider`][fuzzed-data-provider] class provided by LLVM can help you map portions | |
| of the provided `data` to more complex types. | |
| For example: | |
| ```cpp | |
| #include <fuzzer/FuzzedDataProvider.h> | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
| FuzzedDataProvider provider(data, size); | |
| auto flags = provider.ConsumeIntegral<uint32_t>(); | |
| auto name = provider.ConsumeRandomLengthString(); | |
| Parser parser(name, flags); | |
| auto buf = provider.ConsumeRemainingBytes<uint8_t>(); | |
| parser.Parse(buf.data(), buf.size()); | |
| return 0; | |
| } | |
| ``` | |
| There are two notable advantages to using this library: | |
| * First, it makes it easier to rapidly write a fuzzer. | |
| * Second, it is designed to dynamically split inputs in such a way that the fuzzer can | |
| efficiently create new inputs from coverage data. | |
| There is one notable disadvantage: | |
| * Since inputs are dynamically split, it is more difficult to provide a pre-existing [corpus]. It | |
| is still feasible to provide a [dictionary]. | |
| * {Rust} | |
| You can create a fuzz target function that takes one or more inputs with the `Arbitrary` trait | |
| from the [`arbitrary`][arbitrary] crate. This is the recommended approach. | |
| To write a fuzz target function that automatically transforms arbitrary inputs: | |
| 1. If needed, implement the `Arbitrary` trait for the types used by your test code. If possible, | |
| the recommended way to do this is by automatically deriving the trait. Otherwise, this can be | |
| done "by hand" by following the crate's [instructions][arbitrary]. | |
| For example, in your `src/lib.rs`: | |
| ```rust | |
| use arbitrary:Arbitrary; | |
| #[derive(Arbitrary)] | |
| struct ToyStruct { ... } | |
| ``` | |
| 1. Create a function with the [`#[fuzz]`][fuzz-crate] attribute that passes the necessary | |
| parameters to the code you wish to test. | |
| For example, in your `src/lib.rs`: | |
| ```rust | |
| use fuzz::fuzz; | |
| #[fuzz] | |
| fn toy_example_arbitrary(input: ToyStruct) { | |
| let _ = toy_example(input); | |
| } | |
| ``` | |
| * {Go} | |
| You can use the ["encoding/binary"][binary-encoding] package to "cast" or transform bytes to fixed | |
| size types: | |
| ```golang | |
| import ( | |
| "bytes" | |
| "encoding/binary" | |
| ) | |
| func Fuzz(s []byte) { | |
| var s MyStruct | |
| buf := bytes.NewReader(b) | |
| if err := binary.Read(buf, binary.LittleEndian, &s); err != nil { | |
| return | |
| } | |
| mypackage.HandleStruct(s) | |
| } | |
| ``` | |
| Next, you can [build](build-a-fuzzer.md) your newly created fuzzer using GN and Ninja. | |
| [arbitrary]: https://docs.rs/arbitrary/0.4.0/arbitrary | |
| [binary-encoding]: https://golang.org/pkg/encoding/binary/#Read | |
| [corpus]: https://llvm.org/docs/LibFuzzer.html#corpus | |
| [dictionary]: https://llvm.org/docs/LibFuzzer.html#dictionaries | |
| [fuzz-crate]: /src/lib/fuzzing/rust/src/lib.rs | |
| [fuzz-target]: https://llvm.org/docs/LibFuzzer.html#fuzz-target | |
| [fuzzed-data-provider]: https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider |