diff --git a/crates/pdf/src/image.rs b/crates/pdf/src/image.rs index 5e6c49a5..2c8adda6 100644 --- a/crates/pdf/src/image.rs +++ b/crates/pdf/src/image.rs @@ -82,7 +82,10 @@ pub(crate) fn inline_len(file: &File, d: &Dict, res: Option<&Dict>) -> Option Interp<'a> { && (p + 2 == data.len() || crate::object::is_white(data[p + 2])) }; let end = match crate::image::inline_len(self.file, &d, Some(res)) { - Some(len) => (start + len).min(data.len()), + Some(len) => start.saturating_add(len).min(data.len()), None => { let mut p = start; while p + 1 < data.len() && !is_ei(p) { diff --git a/crates/pdf/src/tests_import.rs b/crates/pdf/src/tests_import.rs index e53727ef..7cebcd43 100644 --- a/crates/pdf/src/tests_import.rs +++ b/crates/pdf/src/tests_import.rs @@ -263,6 +263,22 @@ fn hostile_structures_return_errors_or_draw_nothing() { assert!(!doc.skipped.is_empty()); } +#[test] +fn inline_image_with_absurd_dimensions_falls_back_to_ei_scan() { + // `/W 5000000000000000000` overflows the row math (`* bpc` alone exceeds usize::MAX): + // `inline_len` must return `None` (scan for `EI`) instead of overflowing; the oversized + // image itself is then refused by size. + let bytes = page_pdf("q 10 0 0 10 0 0 cm BI /W 5000000000000000000 /H 100 /CS /G /BPC 8 ID \0 EI Q", "", vec![]); + let doc = parse(&bytes).unwrap(); + let _ = render(&doc); + assert!(!doc.skipped.is_empty()); + // A length just under `usize::MAX` passes the row math; past byte 4096 of the content the + // caller's `start + len` would then overflow. + let content = format!("{}q 10 0 0 10 0 0 cm BI /W 2305843009213693440 /H 8 /CS /G /BPC 8 ID EI Q", " ".repeat(5000)); + let doc = parse(&page_pdf(&content, "", vec![])).unwrap(); + let _ = render(&doc); +} + #[test] fn truncated_and_corrupt_eps_never_panic() { let eps = b"%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 100 100\n\