mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 22:17:53 -06:00
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:
co-authored by
Claude Opus 5.5
parent
e2d18b5fb9
commit
63b6de336f
@@ -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;
|
||||
|
||||
@@ -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" | ||||