From 4b7e4ddbb32056dd77e6daa6ef9ded39aae598d3 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Sat, 1 Aug 2026 16:08:11 -0700 Subject: [PATCH] Pin find_boxes_capped's cap boundary and its size-field byte offsets Nothing asserted the scan actually STOPS at cap rather than one match past it, or that the declared box size is decoded from its own four bytes rather than an adjacent one - every existing fixture used sizes small enough that all but the last size byte are zero, so an index slip reading the wrong byte would read the same zero and go unnoticed. --- src/mux/mp4/read.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/mux/mp4/read.rs b/src/mux/mp4/read.rs index 351ae4e..67ad167 100644 --- a/src/mux/mp4/read.rs +++ b/src/mux/mp4/read.rs @@ -2938,6 +2938,46 @@ mod tests { ); } + /// `find_boxes_capped` stops at exactly `cap` matches, not one past it — + /// the whole point being that a caller asking for `cap` matches never + /// forces the scan to walk (and match-collect) further into a crafted + /// payload than it asked for. + #[test] + fn find_boxes_capped_stops_exactly_at_cap_not_one_past() { + let mut payload = Vec::new(); + for _ in 0..3 { + payload.extend_from_slice(&mp4_box(b"test", &[])); + } + let out = find_boxes_capped(&payload, b"test", 2); + assert_eq!( + out.len(), + 2, + "three matches exist in the payload; cap=2 must return exactly 2" + ); + } + + /// The declared box size is decoded from four specific bytes + /// (`payload[pos..pos+4]`, big-endian). A size deliberately chosen so byte + /// 1 differs from byte 0 catches an index slip that rereads byte 0 (or any + /// other wrong offset) instead of the byte the field actually occupies — + /// with a small size every byte but the last is zero, so such a slip would + /// go unnoticed. + #[test] + fn find_boxes_capped_decodes_the_size_field_from_its_own_bytes() { + let want_size: u32 = 0x0001_0010; // byte0=0x00 byte1=0x01 byte2=0x00 byte3=0x10 + let mut payload = vec![0u8; want_size as usize]; + payload[0..4].copy_from_slice(&want_size.to_be_bytes()); + payload[4..8].copy_from_slice(b"test"); + let out = find_boxes_capped(&payload, b"test", 1); + assert_eq!(out.len(), 1, "the declared-size box must be found"); + assert_eq!( + out[0].len() as u32, + want_size - 8, + "the matched payload slice's length must match the size field \ + decoded from its own four bytes" + ); + } + /// An `stsc` entry names a `first_chunk` that may exceed the chunk count the /// `stco` actually declares (a truncated or crafted table). The run it would /// fill has to be clamped to the chunks that exist — indexing `spc` past its