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 fuzzto 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!