- 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.
43 lines
1.4 KiB
Rust
43 lines
1.4 KiB
Rust
//! Feed arbitrary bytes to `CobsDecoder::feed` using an arbitrary
|
|
//! chunking strategy driven by the fuzz input, and confirm:
|
|
//! - the decoder never panics,
|
|
//! - every emitted command respects the bounded payload,
|
|
//! - the total number of commands never exceeds the number of 0x00
|
|
//! sentinels in the stream.
|
|
|
|
#![no_main]
|
|
|
|
use donglora_protocol::framing::CobsDecoder;
|
|
use donglora_protocol::{Command, MAX_PAYLOAD};
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
fuzz_target!(|data: &[u8]| {
|
|
// Split the input: first 32 bytes (if available) drive chunk sizes,
|
|
// the rest is the stream.
|
|
let split = 32.min(data.len());
|
|
let (sched, stream) = data.split_at(split);
|
|
|
|
let mut decoder = CobsDecoder::new();
|
|
let mut cursor = 0;
|
|
let mut sched_idx = 0;
|
|
let mut count = 0;
|
|
while cursor < stream.len() {
|
|
let split_size = if sched_idx < sched.len() {
|
|
((sched[sched_idx] as usize) % 64) + 1
|
|
} else {
|
|
16
|
|
};
|
|
sched_idx += 1;
|
|
let end = (cursor + split_size).min(stream.len());
|
|
decoder.feed(&stream[cursor..end], |cmd| {
|
|
count += 1;
|
|
if let Command::Transmit { payload, .. } = cmd {
|
|
assert!(payload.len() <= MAX_PAYLOAD);
|
|
}
|
|
});
|
|
cursor = end;
|
|
}
|
|
let sentinel_count = stream.iter().filter(|&&b| b == 0).count();
|
|
assert!(count <= sentinel_count);
|
|
});
|