From 627d822709fc2d93931f8665c338a9f9365bbd3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Cie=C5=9Blik?= <132496025+Hathor875@users.noreply.github.com> Date: Sat, 10 Oct 2026 12:12:10 +0200 Subject: [PATCH] create: lift TIFF decoding buffer limit to allow print-sized images (#665) (#681) * create: lift TIFF decoding buffer limit to allow print-sized images (#665) By default, the tiff crate limits decoded buffer size to 256 MiB. Set Decoder::with_limits(Limits::unlimited()) to match PNG decoding behavior and prevent 'decoder limits exceeded' on large images. * create: cap the TIFF decode buffer at 2 GiB instead of lifting it entirely (#665) A few bytes claiming enormous dimensions must still be refused, not allocated. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Brandon Thomas Co-authored-by: Claude Opus 5.5 --- contributors/people.toml | 4 ++++ crates/create/src/lib.rs | 12 ++++++++++-- crates/create/src/tests.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/contributors/people.toml b/contributors/people.toml index 566639db..8ba666cc 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -67,3 +67,7 @@ sync_github_name = false [people.Byxis] sync_github_name = true + +[people.Hathor875] +real_name = "Krzysztof Cieślik" +sync_github_name = true diff --git a/crates/create/src/lib.rs b/crates/create/src/lib.rs index ad213c08..0276896e 100644 --- a/crates/create/src/lib.rs +++ b/crates/create/src/lib.rs @@ -303,13 +303,21 @@ fn decoded(name: &str, bytes: &[u8], format: image::ImageFormat) -> Result Result, CreateError> { use tiff::ColorType as C; - use tiff::decoder::{Decoder, DecodingResult}; + use tiff::decoder::{Decoder, DecodingResult, Limits}; use tiff::tags::Tag; let bad = |m: String| CreateError::Image(name.into(), m); - let mut dec = Decoder::new(std::io::Cursor::new(bytes)).map_err(|e| bad(e.to_string()))?; + // The tiff crate stops at 256 MiB of decoded pixels, below an ordinary poster scan (#665): + // 12000x16000 CMYK is 768 MiB. Raise it, but keep a ceiling so a tiny file claiming + // enormous dimensions can't ask for unbounded memory. + let mut limits = Limits::default(); + limits.decoding_buffer_size = MAX_TIFF_DECODED_BYTES; + let mut dec = Decoder::new(std::io::Cursor::new(bytes)).map_err(|e| bad(e.to_string()))?.with_limits(limits); let mut out = Vec::new(); loop { let (w, h) = dec.dimensions().map_err(|e| bad(e.to_string()))?; diff --git a/crates/create/src/tests.rs b/crates/create/src/tests.rs index 9edc3fac..447f3304 100644 --- a/crates/create/src/tests.rs +++ b/crates/create/src/tests.rs @@ -168,6 +168,36 @@ fn bmp_gif_and_multi_page_tiff_images() { assert!(matches!(from_images(&[("x.webp".into(), b"RIFF0000WEBP".to_vec())]), Err(CreateError::Image(..)))); } +/// #665 lifted the tiff crate's 256 MiB decode limit; a few bytes claiming a 60000x60000 page +/// (3.6 GB of gray pixels) must still be refused, not allocated. +#[test] +fn a_tiff_claiming_enormous_dimensions_is_refused() { + let entries: [(u16, u16, u32); 9] = [ + (256, 4, 60_000), // ImageWidth + (257, 4, 60_000), // ImageLength + (258, 3, 8), // BitsPerSample + (259, 3, 1), // Compression: none + (262, 3, 1), // BlackIsZero + (273, 4, 122), // StripOffsets: just past the IFD + (277, 3, 1), // SamplesPerPixel + (278, 4, 60_000), // RowsPerStrip + (279, 4, 1), // StripByteCounts + ]; + let mut tif = b"II*\0".to_vec(); + tif.extend_from_slice(&8u32.to_le_bytes()); + tif.extend_from_slice(&(entries.len() as u16).to_le_bytes()); + for (tag, kind, value) in entries { + tif.extend_from_slice(&tag.to_le_bytes()); + tif.extend_from_slice(&kind.to_le_bytes()); + tif.extend_from_slice(&1u32.to_le_bytes()); + tif.extend_from_slice(&value.to_le_bytes()); + } + tif.extend_from_slice(&0u32.to_le_bytes()); + assert_eq!(tif.len(), 122); + tif.push(0); + assert!(matches!(from_images(&[("huge.tif".into(), tif)]), Err(CreateError::Image(..)))); +} + #[test] fn images_export_as_jpeg_unchanged_and_others_as_png() { let doc = reopen(