52 lines
2.2 KiB
Rust
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)
|
|
}
|