raw: CR3 preview box scan checks 32-bit lengths for overflow (#2569)

On wasm32 a PRVW/THMB box or JPEG length near u32::MAX wrapped j + len and
start + size, so both bounds checks passed and the slice panicked (abort in
the web build). Use checked_add and bytes.get, and add a regression test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ti8KemZbjE2ZgHgV1q1BM
This commit is contained in:
radialmonster
2026-10-28 13:50:04 -04:00
co-authored by Claude Opus 5.5
parent e2d18b5fb9
commit 63b6de336f
2 changed files with 18 additions and 2 deletions
+4 -2
View File
@@ -438,10 +438,12 @@ fn cr3_preview_boxes(bytes: &[u8]) -> Option<&[u8]> {
let be32 = |at: usize| bytes.get(at..at + 4).map(|s| u32::from_be_bytes([s[0], s[1], s[2], s[3]]) as usize);
let (Some(size), Some(len)) = (be32(start), be32(start + 20)) else { continue };
let j = start + 24;
if len < 4 || j + len > start + size.max(24) || j + len > bytes.len() {
// 32-bit box lengths can wrap `usize` on wasm32: check the sums, then slice with `get`
let (Some(end), Some(lim)) = (j.checked_add(len), start.checked_add(size.max(24))) else { continue };
if len < 4 || end > lim {
continue;
}
let jpeg = &bytes[j..j + len];
let Some(jpeg) = bytes.get(j..end) else { continue };
// One that doesn't fit the budget left is skipped; a smaller one later may still fit.
if walked + jpeg.len() > CR3_MAX_WALK {
continue;
+14
View File
@@ -199,6 +199,20 @@ fn picks_largest_dct_jpeg() {
}
}
/// #2569: `PRVW` box and JPEG lengths near `u32::MAX` (which wrap `usize` on wasm32) find no preview, never panic.
#[test]
fn cr3_preview_box_huge_lengths_are_rejected() {
let j = fake_jpeg(300);
for (size, len) in [(u32::MAX, u32::MAX), (u32::MAX, u32::MAX - 23), (24 + j.len() as u32, u32::MAX - 3)] {
let mut cr3 = b"ftypcrx crx isom".to_vec();
cr3.extend_from_slice(&size.to_be_bytes());
cr3.extend_from_slice(b"PRVWT8");
cr3.extend_from_slice(&len.to_be_bytes());
cr3.extend_from_slice(&j);
assert_eq!(cr3_preview_boxes(&cr3), None, "size {size:#x}, len {len:#x}");
}
}
/// A JPEG like [`fake_jpeg`] whose frame header says `w` x `h` with `nc` components.
fn sized_jpeg(w: u16, h: u16, nc: u8, n: usize) -> Vec<u8> {
let mut j = vec![0xff, 0xd8, 0xff, 0xc0, 0x00, 8 + 3 * nc, 8];