Getting Started

Prerequisites

First, remember that you need to run either Linux or macOS, as Windows is not yet supported.

Fuzzcheck comes with a helper tool that helps you pass the right flags to compile fuzz targets. It is recommended to use it. To install it, run:

cargo install cargo-fuzzcheck

It is also necessary to use Rust nightly. You can install it using rustup:

rustup install nightly

What type of function can I fuzz-test?

1) Pure, fast functions

It is best if the tested function is “pure”, meaning that it behaves in exactly the same way every time it is called with the same input. It is also best if the tested function runs very fast, ideally in less than a tenth of a millisecond. This is because fuzzcheck often needs hundreds of thousands of iterations to build a corpus of interesting test cases.

Functions that perform file or network operations may have unpredictable behaviour or take too long to run and are thus not a great fit for fuzz-testing.

Note also that the tested function takes an immutable reference as argument. It is important that it does not mutate its argument through types such as Cell, RefCell, or UnsafeCell.

2) No dependence on information that is lost when cloning

Furthermore, be aware that some types lose information when serialised or cloned. This is the case for Vec<T> and its capacity property. If the tested function changes its behaviour based on these kinds of properties, fuzzcheck will be less useful. For a concrete example, imagine that the tested function is:


#![allow(unused)]
fn main() {
fn test_vector_capacity(v: &Vec<T>) -> bool {
	v.capacity < 10
}
}

Then an empty vector with a capacity of 12 would fail the test. But fuzzcheck will save this failing test case as:

[]

And when the test case is deserialised, the resulting vector may have a capacity of 0, and thus will not fail the test anymore.

Problems also arise when the tested function makes internal decisions based on capacity:


#![allow(unused)]
fn main() {
fn test_vector(v: &Vec<T>) -> bool {
	if v.capacity < 10 {
		// do something
	} else {
		// do something else
	}
}
}

Now the problem is more subtle. Imagine that test_vector is called on an empty vector with capacity 12. Then the second branch will be taken and fuzzcheck may judge that the test case is interesting because it explored a previously uncovered code region. The test case is thus saved to the internal pool by cloning. But cloning does not preserve the capacity property. The vector that is saved in fuzzcheck’s pool is an “impostor” in some sense. This may stall the fuzzer’s progress.

Getting Started

To learn how to use fuzzcheck, go to the quick start section or follow one of the tutorials:

  • Tutorial 1 sets up a differential property test between a binary search tree and std::collections::BTreeSet. Through it, you can learn about the different parts of the fuzzer and learn to interpret the files generated by it. No bug is directly found in this tutorial, but we guess a potential stack overflow by looking at the content of the generated corpus.
  • Tutorial 2 fuzz-test the pulldown-cmark crate using a grammar-based mutator. We find test cases that trigger a panic at multiple different locations.