Skip to content
File

Blob: firmware/vendor/str0m/src/_internal_test_exports/fuzz.rs

rust247 lines
1//! Exported fuzz targets to get them part of the compilation with feature `_internal_test_exports`.
2 
3use std::collections::VecDeque;
4use std::net::{IpAddr, Ipv4Addr, SocketAddr};
5use std::time::{Duration, Instant};
6 
7use crate::change::{SdpAnswer, SdpOffer};
8use crate::format::Codec;
9use crate::net::{Protocol, Receive};
10use crate::packet::{CodecDepacketizer, CodecExtra, Depacketizer, DepacketizingBuffer, RtpMeta};
11use crate::rtp_::{Frequency, MediaTime, Rtcp, RtcpFb, RtpHeader};
12use crate::streams::register::ReceiverRegister;
13use crate::streams::rtx_cache_buf::EvictingBuffer;
14use crate::{Input, Output, Rtc};
15 
16use super::Rng;
17#[allow(unused)]
18use super::setup::{random_config, random_extmap};
19 
20pub fn rtx_buffer(data: &[u8]) {
21 if data.len() < 4 {
22 return;
23 }
24 
25 let buf_size = u16::from_be_bytes([data[0], data[1]]);
26 let max_age = data[2] as u64;
27 let max_size = data[3] as usize;
28 let mut buf = EvictingBuffer::new(buf_size as usize, Duration::from_secs(max_age), max_size);
29 let mut now = Instant::now();
30 let mut pos = 0;
31 
32 for d in &data[4..] {
33 now += Duration::from_millis(*d as u64);
34 if d % 2 == 0 {
35 buf.maybe_evict(now)
36 } else {
37 pos += *d as u64;
38 buf.push(pos, now, d);
39 }
40 }
41}
42 
43pub fn rtp_header(data: &[u8]) -> Option<()> {
44 let mut rng = Rng::new(data);
45 let exts = random_extmap(&mut rng, 10)?;
46 let len = rng.usize(76)?;
47 RtpHeader::_parse(rng.slice(len)?, &exts);
48 Some(())
49}
50 
51pub fn rtp_packet(data: &[u8]) -> Option<()> {
52 let mut rng = Rng::new(data);
53 let exts = random_extmap(&mut rng, 10)?;
54 // Maximum RTP packet size is typically around 1500 bytes (MTU)
55 let packet_len = rng.usize(1500)?;
56 let packet = rng.slice(packet_len)?;
57 
58 // Parse the header from the packet
59 let header = RtpHeader::_parse(packet, &exts)?;
60 
61 // Extract the payload (data after the header)
62 let _payload = packet.get(header.header_len..)?;
63 
64 Some(())
65}
66 
67pub fn sdp_offer(data: &[u8]) -> Option<()> {
68 let str = std::str::from_utf8(data).ok()?;
69 let _ = SdpOffer::from_sdp_string(str);
70 Some(())
71}
72 
73pub fn sdp_answer(data: &[u8]) -> Option<()> {
74 let str = std::str::from_utf8(data).ok()?;
75 let _ = SdpAnswer::from_sdp_string(str);
76 Some(())
77}
78 
79pub fn depack(data: &[u8]) -> Option<()> {
80 let mut rng = Rng::new(data);
81 
82 let codec = match rng.u8(10)? {
83 0 => Codec::Opus,
84 1 => Codec::Vp8,
85 2 => Codec::Vp9,
86 3 => Codec::H264,
87 4 => Codec::H265,
88 5 => Codec::H266,
89 6 => Codec::Av1,
90 7 => Codec::PCMU,
91 8 => Codec::PCMA,
92 9 => Codec::G722,
93 10 => Codec::CN,
94 _ => unreachable!(),
95 };
96 
97 let mut depack = DepacketizingBuffer::new(codec.into(), rng.usize(300)?);
98 
99 let exts = random_extmap(&mut rng, 10)?;
100 
101 let start = Instant::now();
102 
103 loop {
104 let do_push = rng.bool()?;
105 
106 if do_push {
107 let hlen = rng.usize(76)?;
108 let header = RtpHeader::_parse(rng.slice(hlen)?, &exts)?;
109 let meta = RtpMeta {
110 received: start + Duration::from_millis(rng.u64(10000)?),
111 time: MediaTime::new(rng.u64(u64::MAX)?, Frequency::MICROS),
112 seq_no: rng.u64(u64::MAX)?.into(),
113 header,
114 last_sender_info: None,
115 };
116 let len = rng.usize(1200)?;
117 let data = rng.slice(len)?.to_vec();
118 depack.push(meta, data);
119 } else {
120 depack.pop();
121 }
122 }
123}
124 
125pub fn receive_register(data: &[u8]) -> Option<()> {
126 let mut rng = Rng::new(data);
127 let mut rr = ReceiverRegister::new(None);
128 let start = Instant::now();
129 loop {
130 match rng.u8(2)? {
131 0 => {
132 let seq = rng.u64(u64::MAX / 2)?;
133 let arrival = start + Duration::from_micros(rng.u64(u64::MAX / 100)?);
134 let rtp_time = rng.u32(u32::MAX / 2)?;
135 let clock_rate = rng.u32(u32::MAX / 2)?;
136 rr.update(seq.into(), arrival, rtp_time, clock_rate);
137 }
138 1 => {
139 rr.nack_report();
140 }
141 2 => {
142 rr.reception_report();
143 }
144 _ => unreachable!(),
145 }
146 }
147}
148 
149/// Fuzz the full `Rtc` receive path: instance init, datagram demux
150/// (STUN/DTLS/RTP/RTCP), dispatch, and `poll_output` draining.
151pub fn receive(data: &[u8]) -> Option<()> {
152 let mut rng = Rng::new(data);
153 
154 let base = Instant::now();
155 let mut rtc = Rtc::new(base);
156 
157 let source = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 5000);
158 let destination = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(2, 2, 2, 2)), 5001);
159 
160 let mut millis: u64 = 0;
161 
162 loop {
163 // Drive time monotonically forward.
164 millis += rng.u64(50)?;
165 let now = base + Duration::from_millis(millis);
166 
167 if rng.bool()? {
168 // Feed a datagram through the full demux + dispatch path.
169 let len = rng.usize(2000)?;
170 let buf = rng.slice(len)?;
171 if let Ok(receive) = Receive::new(Protocol::Udp, source, destination, buf) {
172 let _ = rtc.handle_input(Input::Receive(now, receive));
173 }
174 } else {
175 let _ = rtc.handle_input(Input::Timeout(now));
176 }
177 
178 // Drain outputs (bounded to avoid spinning on a single input).
179 for _ in 0..1000 {
180 match rtc.poll_output() {
181 Ok(Output::Timeout(_)) => break,
182 Ok(_) => continue,
183 Err(_) => break,
184 }
185 }
186 
187 if !rtc.is_alive() {
188 return Some(());
189 }
190 }
191}
192 
193/// Fuzz STUN message parsing (the ICE input path).
194pub fn stun(data: &[u8]) -> Option<()> {
195 let _ = is::stun::StunMessage::parse(data);
196 Some(())
197}
198 
199/// Fuzz RTCP compound-packet parsing and the feedback conversion.
200pub fn rtcp(data: &[u8]) -> Option<()> {
201 let mut feedback = VecDeque::new();
202 Rtcp::read_packet(data, &mut feedback);
203 // Exercise the feedback conversion as well.
204 let _ = RtcpFb::from_rtcp(feedback.drain(..)).count();
205 Some(())
206}
207 
208/// Fuzz each codec depacketizer directly (bypassing the buffer's gating),
209/// exercising the raw `Depacketizer::depacketize` parsers.
210pub fn depack_direct(data: &[u8]) -> Option<()> {
211 let mut rng = Rng::new(data);
212 
213 let codec = match rng.u8(10)? {
214 0 => Codec::Opus,
215 1 => Codec::Vp8,
216 2 => Codec::Vp9,
217 3 => Codec::H264,
218 4 => Codec::H265,
219 5 => Codec::H266,
220 6 => Codec::Av1,
221 7 => Codec::PCMU,
222 8 => Codec::PCMA,
223 9 => Codec::G722,
224 10 => Codec::CN,
225 _ => unreachable!(),
226 };
227 
228 let mut depack: CodecDepacketizer = codec.into();
229 let mut out = Vec::new();
230 let mut extra = CodecExtra::None;
231 
232 loop {
233 let marker = rng.bool()?;
234 let len = rng.usize(1500)?;
235 let packet = rng.slice(len)?;
236 
237 let _ = depack.depacketize(packet, &mut out, &mut extra);
238 let _ = depack.is_partition_head(packet);
239 let _ = depack.is_partition_tail(marker, packet);
240 
241 // Keep memory bounded across iterations.
242 if out.len() > 4_000_000 {
243 out.clear();
244 }
245 }
246}