Fuzzing unsafe code in a Rust crate

Adrian Taylor
2 min readMar 4, 2022

--

Nearly all Rust code is memory-safe. A necessary part of using Rust is to use the wide ecosystem of third-party Rust crates. These are terrific! — but some of them, especially the lower-level crates, have unsafe code.

Fuzzing stands a reasonable chance of finding any bugs in that unsafe code, so here’s how to add a fuzzer end-to-end, using itoa as an example.

  1. Figure out the crate with the unsafe code which you want to fuzz.
  2. Fetch it from github.
  3. Check that you can build it, etc. cargo test
  4. Create a directory called fuzz .
  5. Add a Cargo.toml, like this:

6. Add fuzz_targets/fuzz_itoa.rs (or some suitable equivalent name) with boilerplate code like this:

7. Simply run cargo fuzz run fuzz_itoa . (You may need to cargo install cargo-fuzz first).

You should see the fuzzer exploring the codebase. Except, of course, it won’t because you’re not actually calling any APIs of the crate.

Now you get to do the fun bit. Really, you’re going to like this.

The purpose of a fuzzer is to provide different permutations of input into the APIs such that all possible code paths are explored. Coverage-guided fuzzers do this by carefully evolving input data as they watch the code coverage change.

So your goal now is to fill out the fuzzer to convert from arbitrary data to every conceivable API call. The fuzzing infrastructure will then carefully evolve the data to work out how to explore the codebase.

Fortunately, the Arbitrary crate makes this a delight in Rust. Here’s all we have to do for itoa:

As far as I can tell, that simple code covers 100% of the API surface of itoa. So now back to our steps:

8. Run the fuzzer again. Let’s see what it says:

Note the mentions of ‘cov’ and ‘ft’. ‘cov’ is the total number of “code blocks” explored and ‘ft’ is the total number of features. If you leave your fuzzer running, these numbers should gradually climb and reach a plateau as the fuzzing engine has figured out how to explore all of itoa’s accessible APIs.

(In this particular case, the fuzzer eventually explored 519 code blocks and 3355 features. It didn’t find any bugs!)

Of course, if you’re lucky, you’ll see a crash during this process — in which case, you’ve found a bug, report it! With some crates you may be able to find a wider range of bugs by using address sanitizer:

RUSTFLAGS="-Z sanitizer=address" cargo fuzz run fuzz_itoa

Finally,

  • Raise a pull request against the project to add your fuzzer.
  • Once the project has accepted the pull request, also raise a pull request against OSS-Fuzz to get these fuzzers run on their infrastructure forever. As the crates evolve, new code will be fuzzed, and bugs filed if problems are found. (Their Rust-specific guide).
  • Maybe some of the unsafe code can be removed?

--

--

Adrian Taylor

Ade works on Chrome at Google, and likes mountain biking, climbing, snowboarding, and usually his kids. All opinions are my own.