Files
protocol-rs/fuzz/fuzz_targets/cobs_chunk_invariant.rs
T
swaits ef576ba1ec test: three more cargo-fuzz targets + test-fuzz-long recipe
- command_roundtrip: structure-aware (via arbitrary crate) Command
  encode → parse → equal. Symmetric to cobs_roundtrip.
- cobs_decoder_multi_frame: chunked feeding with arbitrary split sizes;
  asserts bounded payload and that command count never exceeds sentinel
  count.
- cobs_chunk_invariant: same stream through two decoders (chunked vs
  all-at-once) must produce the same command sequence.

Adds test-fuzz-long recipe (600s per target, ~1h 10m total) for CI
cron / pre-release. All seven targets smoke-tested at 100k runs each
with zero findings.
2026-04-13 12:12:12 -06:00

42 lines
1.1 KiB
Rust

//! Chunk-boundary invariance: the same input stream fed into two
//! decoders — one in arbitrary chunks, one all-at-once — must produce
//! the same command sequence.
#![no_main]
use donglora_protocol::Command;
use donglora_protocol::framing::CobsDecoder;
use libfuzzer_sys::fuzz_target;
fn drain(stream: &[u8], chunk_sched: &[u8]) -> Vec<Command> {
let mut out: Vec<Command> = Vec::new();
let mut decoder = CobsDecoder::new();
let mut cursor = 0;
let mut idx = 0;
while cursor < stream.len() {
let split = if idx < chunk_sched.len() {
((chunk_sched[idx] as usize) % 64) + 1
} else {
16
};
idx += 1;
let end = (cursor + split).min(stream.len());
decoder.feed(&stream[cursor..end], |c| out.push(c));
cursor = end;
}
out
}
fuzz_target!(|data: &[u8]| {
let split = 32.min(data.len());
let (sched, stream) = data.split_at(split);
let whole: Vec<Command> = {
let mut v = Vec::new();
CobsDecoder::new().feed(stream, |c| v.push(c));
v
};
let chunked = drain(stream, sched);
assert_eq!(whole, chunked);
});