blob: 6f48a21cdb613e786445f140f0502a7e394ad9f7 [file] [log] [blame] [view]
# 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]{:.external} 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>;
```
## 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 have the signature
`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`.
## 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]{:.external} 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][corpus]{:.external}. It is still feasible to provide a
[dictionary][dictionary]{:.external}.
* {Rust}
You can create a fuzz target function that takes one or more inputs with the `Arbitrary` trait
from the [`arbitrary`][arbitrary]{:.external} 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]{:.external}.
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);
}
```
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
[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/HEAD/docs/split-inputs.md#fuzzed-data-provider