midi-harbor/fuzz/fuzz_targets/rtpmidi_packet.rs
2026-09-28 13:59:10 -05:00

52 lines
2.2 KiB
Rust

//! Feeds hostile datagrams to the two RTP-MIDI parsers a peer on the network can reach.
//!
//! Both ports read whatever arrives, so the control parser and the data parser each get every
//! input. Neither may allocate more than a small multiple of what it was sent, and whatever either
//! accepts has to come back unchanged after being encoded and parsed again: a packet the daemon
//! would read one way and send another is a peer misreading us.
#![no_main]
use libfuzzer_sys::fuzz_target;
use midi_harbor_rtpmidi::{ControlPacket, RtpMidiPacket};
/// Heap bytes a parse may hold per byte of input, which covers a vector doubling past a message
/// list at one message per input byte.
const BYTES_PER_INPUT_BYTE: u64 = 64;
/// Heap bytes a parse may hold whatever its input, for fixed-size bookkeeping.
const FIXED_BYTES: u64 = 4096;
fuzz_target!(
init: {
// The allocation checks below prove nothing unless the counter is really installed.
let probe = allocation_counter::measure(|| drop(std::hint::black_box(vec![0_u8; 64])));
assert!(probe.count_total > 0, "the allocation counter is not installed");
},
|data: &[u8]| {
let bound = bound(data);
let mut control = None;
let allocations = allocation_counter::measure(|| control = ControlPacket::parse(data).ok());
assert!(allocations.bytes_max <= bound, "control parse held {allocations:?}");
if let Some(packet) = control {
assert_eq!(ControlPacket::parse(&packet.encode()), Ok(packet));
}
let mut rtp = None;
let allocations = allocation_counter::measure(|| rtp = RtpMidiPacket::parse(data).ok());
assert!(allocations.bytes_max <= bound, "rtp parse held {allocations:?}");
if let Some(packet) = rtp {
assert!(packet.messages.len() <= data.len(), "more messages than bytes");
assert_eq!(RtpMidiPacket::parse(&packet.encode()), Ok(packet));
}
}
);
/// The most heap a parse of `data` may hold at once.
fn bound(data: &[u8]) -> u64 {
u64::try_from(data.len())
.unwrap_or(u64::MAX)
.saturating_mul(BYTES_PER_INPUT_BYTE)
.saturating_add(FIXED_BYTES)
}