Skip to content

Latest commit

 

History

History
92 lines (61 loc) · 3.63 KB

Fuzz-Testing.md

File metadata and controls

92 lines (61 loc) · 3.63 KB

Fuzzing Parity Code

Fuzzing is a great tool for coverage testing, and discovering bugs. Rust has a utility (cargo-fuzz) for tying into the powerful libFuzzer and sanitizer libraries packaged with LLVM.

Installing Cargo-Fuzz

Cargo-fuzz is a tool available through the cargo toolchain, and requires the nightly compiler. Ensure that the nightly compiler is available on the system where cargo-fuzz is going to be installed.

For example, the following commands utilize rustup and cargo install to setup a fuzzing environment:

rustup install nightly
rustup default nightly
cargo install cargo-fuzz

Initializing Fuzz Tests

Cargo-fuzz needs a special file structure to work properly. Luckily, cargo-fuzz comes packed with a tool that makes this setup very simple.

Navigate to the root directory of the package you want to test, then run:

cd /path/to/package/root
cargo-fuzz init [-t first_test_name]

After running the above commands, you should have a fuzz directory in the current working directory. This directory contains everything cargo-fuzz will need to start fuzzing the package!

Under the fuzz/fuzz_targets directory, there should be a single Rust source file. If no initial target is provided (the -t flag used during cargo-fuzz init), the first test should be called fuzz_target_1.rs. If a initialization target was provided, the first file will be the supplied target name.

Running Fuzz Tests

Running fuzz tests is similarly easy to setting up the fuzz directory. From the same directory where the fuzz tests were initialized, run the following command:

cargo-fuzz run <test_name> # do NOT include the .rs from the file name

Since the test harness doesn't include any fuzzing code yet, the test will run indefinitely (no crashes will be found). However, some interesting output will be generated by cargo-fuzz, and the fuzzer will actually run the empty test harness.

For details on cargo-fuzz output, check the great write-up here.

Writing Fuzz Tests

So, the tools are installed, the project directory is instrumented, and there is an empty fuzzing harness. Now what?

Let's fill in the blanks using a simple test case, fuzzing the keccak hash library used by Parity.

First, repeat the steps to initialize a new fuzzing project:

cd /path/to/parity/util/hash
rustup run nightly bash # run a bash shell setup for nightly Rust
cargo-fuzz init -t keccak

Now, there should be a fuzz directory with an empty fuzz harness called keccak.rs. Let's open the file:

(fuzz/fuzz_targets/keccak.rs)
#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate keccak_hash;

fuzz_target!(|data: &[u8]| {
    // fuzz code goes here
});

Most of this is boilerplate we don't have to worry about for now. If we look inside util/hash/lib.rs, we find the function we're interested in pub fn keccak<T: AsRef<[u8]>>(s: T) -> H256. Well this is just too easy, keccak() takes a ref pointer to a u8 slice for input, which is just what cargo-fuzz provides via the fuzz_target!() macro.

Now we just need to make keccak() visible to our fuzzer, and supply it with the input supplied in data:

#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate keccak_hash;
use keccak_hash::keccak;

fuzz_target!(|data: &[u8]| {
    keccak(data);
});

Then, just run the test from the util/hash directory:

cargo-fuzz run keccak -- -runs=100

The extra flags on the end are to pipe options to libFuzzer. In this case, we are telling cargo-fuzz to stop after 100 runs. Otherwise, it would run until it finds a crash (unlikely for such a simple fuzzing test).