Four libFuzzer targets: - parse_command: Command::from_bytes never panics; bounded payload. - parse_radio_config: RadioConfig::from_bytes never panics. - cobs_decoder_feed: CobsDecoder::feed is panic-free across arbitrary byte streams split into arbitrary chunks. - cobs_roundtrip: structure-aware (via arbitrary crate) Response → cobs_encode_response → ucobs::decode yields identical raw bytes. All four smoke-tested at 50k runs each with no findings. Intended CI cadence is a weekly cron with a larger time budget; per-PR would be expensive. Requires nightly + cargo-fuzz.
37 lines
1.2 KiB
Rust
37 lines
1.2 KiB
Rust
//! `CobsDecoder::feed` must never panic or yield an oversized payload
|
|
//! when fed arbitrary bytes in arbitrary chunks.
|
|
//!
|
|
//! The input is split into chunks driven by a leading length-prefix
|
|
//! stream so the fuzzer can exercise boundary handling.
|
|
|
|
#![no_main]
|
|
|
|
use donglora_protocol::MAX_PAYLOAD;
|
|
use donglora_protocol::framing::CobsDecoder;
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
fuzz_target!(|data: &[u8]| {
|
|
let mut decoder = CobsDecoder::new();
|
|
let mut cursor = 0;
|
|
|
|
// Use the first byte (if any) as a pseudo-chunk-schedule: low nibble
|
|
// picks a split size 1..=16, high nibble is skipped. Subsequent
|
|
// leading bytes drive subsequent splits.
|
|
let mut sched_idx = 0;
|
|
while cursor < data.len() {
|
|
let split = if sched_idx < data.len() {
|
|
((data[sched_idx] & 0x0F) as usize) + 1
|
|
} else {
|
|
16
|
|
};
|
|
sched_idx = sched_idx.saturating_add(1);
|
|
let end = (cursor + split).min(data.len());
|
|
decoder.feed(&data[cursor..end], |cmd| {
|
|
if let donglora_protocol::Command::Transmit { payload, .. } = cmd {
|
|
assert!(payload.len() <= MAX_PAYLOAD);
|
|
}
|
|
});
|
|
cursor = end;
|
|
}
|
|
});
|