* 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 <noreply@anthropic.com> --------- Co-authored-by: Brandon Thomas <echelon@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a211e334a0
commit
627d822709
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -303,13 +303,21 @@ fn decoded(name: &str, bytes: &[u8], format: image::ImageFormat) -> Result<Embed
|
|||
Ok(rgba_image(rgba.as_raw(), rgba.dimensions(), (72.0, 72.0)))
|
||||
}
|
||||
|
||||
/// Most decoded bytes one TIFF page may take: room for the 768 MiB CMYK poster in #665, twice over.
|
||||
const MAX_TIFF_DECODED_BYTES: usize = 2 << 30;
|
||||
|
||||
/// Every page of a TIFF (multi-page scans become multi-page PDFs).
|
||||
fn tiff_pages(name: &str, bytes: &[u8]) -> Result<Vec<Embedded>, 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()))?;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue