increment function that increases a counter value by one on every invocation.
How to Write Fuzz Tests with cargo-fuzz
-
Install the nightly Rust toolchain. Nightly Rust is required to run cargo-fuzz.
-
Install
cargo-fuzz. -
Initialize a fuzz project by running the following command inside your contract directory.
-
Open the contract’s
Cargo.tomlfile. Addlibas acrate-type. -
Open the generated
fuzz/Cargo.tomlfile. Add thesoroban-sdkdependency. -
Open the generated
fuzz/src/fuzz_target_1.rsfile. It will look like the below. -
Fill out the
fuzz_target!call with test setup and assertions. For example, for the increment example: -
Execute the fuzz target.
If you’re developing on MacOS you need to add the
--sanitizer=threadflag in order to work around a known issue.
- Create an environment, the
Env. - Register the contract to be tested.
- Invoke functions using a client.
- Assert expectations.
How to Write Fuzz Tests with cargo-afl
cargo-afl drives AFL++ and, unlike cargo-fuzz, runs on stable Rust.
-
Install
cargo-afl. Installing it builds AFL++ from source, so a C compiler needs to be available. -
Configure the machine for fuzzing.
-
Open the contract’s
Cargo.tomlfile. Addlibas acrate-type. The fuzz target imports the contract as a Rust library. -
Create a fuzz target crate, for example with
cargo new --bin fuzzinside your contract’s directory. Unlike acargo-fuzztarget, an AFL++ target depends on thearbitrarycrate directly, because thefuzz!macro expands to code that refers to it by an absolute path, which only resolves ifarbitraryis a direct dependency. Put the following in the new crate’sCargo.toml. It replaces the empty[dependencies]table thatcargo newgenerated. -
Write the fuzz target at
src/fuzz_target_1.rs, and delete the defaultsrc/main.rscargo newcreated. An AFL++ target is a regular binary crate with amainfunction that calls theafl::fuzz!macro. For example, for the increment example: -
Build the target. The remaining steps also run from inside the
fuzzcrate. -
Fuzz the target, pointing at an input directory containing at least one seed input and an output directory to write results to.
out/default/crashes/. The target reads an input on stdin when it isn’t being driven by AFL++, so a crash can be replayed by feeding the file back in:
How to Get Code Coverage of cargo-fuzz Tests
Getting code coverage data for fuzz tests requires some different tooling than when doing the same for regular Rust tests.
-
Run the
cargo-fuzztarget until it has produced a corpus. -
Install the llvm-tools for the nightly compiler.
-
Run the fuzz coverage command that’ll execute the corpus and write coverage data to the coverage directory in the
profdataformat. -
Run the llvm-cov command to convert the profdata file to an lcov file.
Load the
lcov.infofile into your IDE using its coverage feature. In VSCode this can be done by installing the Coverage Gutters extension and executing theCoverage Gutters: Watchcommand.