Note: This is an area of active development. The instructions and results below should be considered experimental.
Fuchsia includes experimental support for writing FIDL fuzzers in the style of FIDL unit tests.
An example fuzzer is defined in //examples/fuzzers/fidl
. If you are not familiar with fuzzers, see the overview. 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.
fuzzers = {protocol = "fully.qualified.fidl.ProtocolName"}
to your fidl()
GN target.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.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).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.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).fuzzer
GN target that fuzzes your server.fidl_protocol_fuzzer()
target to fuzzers = [ ... ]
in a new or existing fuzzers_package
GN target.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:
fuzzer_init()
, initializing the server to be fuzzed.zx::channel
s.fidl::InterfacePtr
of the appropriate type, bound to a channel end and the loop's dispatcher.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).fidl::InterfacePtr
.async::Loop
to run-until-idle.zx::event
.fuzzer_disconnect(raw_server_channel_handle, loop->dispatcher())
, allowing the server to clean up its end of the connection.fuzzer_clean_up()
to tear down the server instance.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:
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.
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.
Additionally, past experimental efforts have fuzzed the FIDL compiler using afl-fuzz.
Download and build it, then export AFL_PATH
to be whatever path you downloaded and built it with.
afl-fuzz treats crashes as interesting but the parser currently calls __builtin_trap()
when it encounters invalid syntax. Remove that line in parser.h - it's in the Parser::Fail()
method.
fidl
tool with afl-fuzz's instrumentationClear 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.
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 '@@'
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.