test(proto): add decoder fuzzing

Add isolated cargo-fuzz/libFuzzer infrastructure for irtt-proto, fuzzing
decode_request, decode_open_reply, decode_echo_reply, Params::decode(_with_presence),
and the varint codec against arbitrary and structurally-derived wire input.
Each target's core invariant is that arbitrary bytes must never panic; a
small clean-room seed corpus (generated from irtt-proto's own encoders) and
a bounded nightly smoke CI workflow are included. The fuzz project is its
own isolated cargo workspace and does not affect the root workspace, its
lockfile, or any published crate's dependency graph.
This commit is contained in:
Nils Andreas Svee committed 2026-08-19 23:33:27 +07:00
1 parent 9b70bd8b9d
commit 719073df1c
29 files changed
+766

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
#![no_main]
use irtt_proto::Params;
use libfuzzer_sys::fuzz_target;
/// See `decode_request.rs` for the rationale behind this bound.
const MAX_INPUT_LEN: usize = 128 * 1024;
fuzz_target!(|data: &[u8]| {
if data.len() > MAX_INPUT_LEN {
return;
}
let decoded = Params::decode(data);
let decoded_with_presence = Params::decode_with_presence(data);
// Both entry points share one parser, so when both succeed they must
// agree on the decoded values.
match (&decoded, &decoded_with_presence) {
(Ok(params), Ok(with_presence)) => {
assert_eq!(params, &with_presence.params);
}
(Ok(_), Err(_)) | (Err(_), Ok(_)) => {
panic!("decode and decode_with_presence disagreed on success for {data:02x?}");
}
(Err(_), Err(_)) => {}
}
// No panic is the primary invariant for malformed input; unknown tags are
// tolerated and repeated known tags are last-value-wins by design, so we
// do not assert a particular failure mode here.
if let Ok(params) = decoded {
let encoded = params.encode();
// Re-encoding and decoding again must preserve the semantic value.
// Presence information does not round-trip through `encode` for
// absent/default fields, so we compare `Params` values, not the
// original wire bytes.
if let Ok(reencoded_params) = Params::decode(&encoded) {
assert_eq!(params, reencoded_params);
} else {
panic!(
"re-encoding a successfully decoded Params produced bytes that failed to decode"
);
}
}
});