Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fuzzing the decompressor

While panicking is acceptable in case of a programming error in your code, it should never be due to an incorrect user input, which is what fuzzing will test.

✅ Use cargo fuzz to feed arbitrary data to your decoder.

cargo fuzz requires a nightly toolchain to build the instrumented binaries:

$ rustup toolchain install nightly
$ cargo install cargo-fuzz
$ cargo fuzz init                 # creates a `fuzz/` directory with a first target
$ cargo +nightly fuzz run fuzz_target_1

cargo fuzz init also creates a fuzz/.gitignore file excluding the generated corpus, artifacts and target directories. Before running, seed the corpus with a few valid files (e.g., copy the textbook examples and some zstd-compressed files into fuzz/corpus/fuzz_target_1/): libFuzzer mutates existing inputs, and would otherwise spend a long time discovering the magic numbers by itself.

A fuzz target is a tiny program receiving random bytes; feed them to your library and ignore the errors it returns, only panics and crashes are of interest here:

#![no_main]

use libfuzzer_sys::fuzz_target;
use net7212::{frame::Frame, parsing::ForwardByteParser};

fuzz_target!(|data: &[u8]| {
    let mut input = ForwardByteParser::new(data);
    while !input.is_empty() {
        let Ok(frame) = Frame::parse(&mut input) else { break };
        if frame.decode().is_err() {
            break;
        }
    }
});

Your decoder should never crash. Of course, it might return an error, but the error should be meaningful and not result in a memory corruption.

💫 Congratulations, you now have a robust Zstandard decoder!