blob: 47093a0bb55cf748189f3a700669358536168472 [file] [log] [blame] [view]
# Fuzzing FIDL
Note: This is an area of active development. The instructions and results below should be considered
experimental.
## Fuzzing FIDL servers with libFuzzer on Fuchsia
Fuchsia includes experimental support for writing FIDL fuzzers in the style of FIDL unit tests.
### Quick-start guide
An example fuzzer is defined in [`//examples/fuzzers/fidl`][example]. If you
are not familiar with fuzzers, see the [overview](overview.md). Fuzzing FIDL servers with libFuzzer
on Fuchsia requires GN targets that will generate a fuzz target and writing some code to provide an
instance of the server to be fuzzed.
1. Add `fuzzers = {protocol = "fully.qualified.fidl.ProtocolName"}` to your `fidl()` GN target.
1. Depending on the language you are using:
* At the C++ level (easy mode):
Use the `FIDL_FUZZER_DEFINITION()` macro in `//sdk/lib/fidl/cpp/fuzzing/server_provider.h` to
define a server provider for your interface and server implementation class. This will
automatically define the C symbols described below. See
`//examples/fuzzers/fidl` for a reference example.
* At the C level (hard mode):
Implement a library that defines the following symbols:
* `zx_status_t fuzzer_init()`: Instantiate server implementation.
* `zx_status_t fuzzer_connect(zx_handle_t, async_dispatcher_t*)`: Bind server implementation
to the channel handle. Optionally, use the dispatcher if your server can be fuzzed on the
same thread as fuzzer clients (see [note on threading](#a-note-about-threading)).
* `zx_status_t fuzzer_disconnect(zx_handle_t, async_dispatcher_t*)`: Unbind server
implementation from the channel handle.
* `zx_status_t fuzzer_clean_up()`: Clean up server implementation.
If any of these returns a status other than `ZX_OK`, then the fuzzer will cleanup and halt.
1. Define a `fidl_protocol_fuzzer()` GN target. Specify:
* `fidl = //path/to:fidl_gn_target` (the `fidl()` target mentioned above).
* `protocol = "fully.qualified.fidl.ProtocolName"`.
* `deps = [... :your_library ...]` (the one mentioned above, defining `fuzzer_...` symbols).
* Anything else needed for a [`fuzzer`](build-a-fuzzer.md#fuzzer) GN target that fuzzes your
server.
1. Add `fidl_protocol_fuzzer()` target to `fuzzers = [ ... ]` in a new or existing
[`fuzzer_package`](build-a-fuzzer.md#fuzzer_package) GN target.
### Implementation details
The bulk of a FIDL server implementation fuzz target is C++ code generated by `fidlgen` that expects
a handful of C symbols to provide an API to the FIDL server implementation. The generated code
contains a global `async::Loop`, bound to its initial thread, that is reused for the client side of
a FIDL connection on each run of the fuzz target. `LibFuzzer` repeatedly invokes the same fuzz
target with different inputs. The generated fuzz target code will:
* Invoke `fuzzer_init()`, initializing the server to be fuzzed.
* Instantiates a pair of `zx::channel`s.
* Initializes an `fidl::InterfacePtr` of the appropriate type, bound to a channel end and the loop's
dispatcher.
* Invoke `fuzzer_connect(raw_server_channel_handle, loop->dispatcher())`, establishing a connection
with the server and allowing the server to opt in to using the same dispatcher as the client if
its API is compatible with such a scheme (see [note on threading](#a-note-about-threading)).
* Invoke a method through its `fidl::InterfacePtr`.
* Set its `async::Loop` to run-until-idle.
* Synchronize with the method's callback via a `zx::event`.
* Invoke `fuzzer_disconnect(raw_server_channel_handle, loop->dispatcher())`, allowing the server to
clean up its end of the connection.
* Invoke `fuzzer_clean_up()` to tear down the server instance.
#### Allocation of fuzz target input to FIDL messages
In broad strokes, the first two bytes are used to select a protocol and method pair from among those
defined in the FIDL source file. In the case of a FIDL file that contains many protocols, but only
one is enabled in the fuzzer, discovery of meaningful inputs relies on `LibFuzzer`'s coverage-guided
engine to deduce that the first bytes of a certain form cause the fuzzer to exercise almost no code.
After a protocol and method pair are identified, the remaining bytes are carved up as follows:
* Each type has a trait that defines the minimum number of bytes it requires.
* If insufficient bytes are input, the fuzz target exits immediately.
* Otherwise the "slack" bytes that exceed the minimum required for the method parameters are divided
evenly among parameters and an allocation trait for each type is invoked to construct an object of
the appropriate type using at most `MinForParam + SlackForParam` bytes.
In terms of allocation trait details, collection and numeric types have relatively natural
interpretations based on the collection of bytes to be transformed into an object. Handles are
treated like numeric types, which can be expected to lead to errors when the server attempts to
exercise them.
#### A note about threading
{#a-note-about-threading}
It is **highly** desirable to keep the fuzz target single-threaded. That is, use
`ServerProviderDispatcherMode::kFromCaller` in C++ or use the `async_dispatcher_t*` passed to
`fuzzer_connect` in C. This is preferred because it increases the likelihood that bugs found by the
fuzzer will be consistently reproducible.
## Fuzzing the FIDL host tools with AFL
Additionally, past experimental efforts have fuzzed the FIDL compiler using
[afl-fuzz](http://lcamtuf.coredump.cx/afl/).
### Build afl-fuzz
Download and build it, then `export AFL_PATH` to be whatever path you downloaded and built it with.
### Patch the parser to not trap on invalid syntax
afl-fuzz treats crashes as interesting but the parser currently calls `__builtin_trap()`
when it encounters invalid syntax. Remove that line in
[parser.h](/tools/fidl/fidlc/include/fidl/parser.h) - it's in the `Parser::Fail()` method.
### Build the `fidl` tool with afl-fuzz's instrumentation
Clear any existing build and then build with the afl-fuzz compiler wrappers.
```
cd $ZIRCON_DIR
rm -fr build-x86
PATH=$PWD/prebuilt/downloads/clang+llvm-x86_64-linux/bin/:$PATH:$AFL_PATH make \
build-x86/tools/fidl HOST_TOOLCHAIN_PREFIX=afl-
```
Adjust if you're not building on x86 Linux, etc.
### Run the fuzzer
The parser includes some examples to use as inputs.
As FIDL becomes adopted we can expand our inputs to include all of the different protocols
declared across our tree, but for now we use what's in `tools/fidl/examples`.
```
$AFL_PATH/afl-fuzz -i tools/fidl/examples -o fidl-fuzz-out build-x86/tools/fidl dump '@@'
```
### Results
Running against the source from early May 2017, there were no crashes or hangs after two days
of fuzzing on a fairly fast machine. It ran over 300 million executions.
[example]: /examples/fuzzers/fidl