How to fuzz a Rust program

Manish Goregaokar wrote a brilliant blog post about cargo-fuzz and its use in fuzzing Rust programs.

Since that post, I have pushed code to update the default fuzz target template and to make the whole fuzzing experience even nicer. I also cloned a load of open-source projects and ran cargo-fuzz against them... with success. As of today, I’ve submitted 10 bugs to the trophy case; 6 of which are fixed and 4 with open issues on the projects.

If you have a project that takes input, I urge you to fuzz test your code!

Anyway — this is the typical workflow I run through to test a project:

$ cd <your-amazing-project>
$ cargo install cargo-fuzz # (if not already installed)
$ cargo fuzz init
$ vim fuzz/fuzzers/fuzzer_script_1.rs

<edit and save the file... more below>

$ cargo fuzz run fuzzer_script_1

<sit back and wait>

... and that’s it.

/u/lifthrasiir has some good tips regarding command-line flags to speed up fuzzing.

In terms of what to put in the fuzz target, it's simple. You’re typically going to want to add code in here that is the entry point to the rest of the library. (You can, of course, test a certain feature of your program but at least here it’ll test the whole thing.)

An example of such code is below:

#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate your_lib;

fuzz_target!(|data: &[u8]| {
    let _ = your_lib::parse(data); // I added this line.
});

"... But my code doesn’t take a &[u8]", you say — Easy, you can use the following to convert it to a &str.

if let Ok(utf8) = std::str::from_utf8(data) {
    let _ = your_lib::parse(utf8);
}

Now, from here you can either submit an issue to the project or fix the bug yourself. I typically use the following format to submit crashes I have found (example here):

Found using `cargo-fuzz`.

<a minimal program (containing input) which causes the issue>

<backtrace>

I’ll soon be publishing another post about analysing and fixing crashes so keep an eye out — that’s all for now, thanks guys!