diff --git a/Cargo.lock b/Cargo.lock index 27cb0d4e..fabd85f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2613,8 +2613,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "krilla" version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27da593198b20eeba65caeb73c2bbeec3e53ab08fa549898312ce81c4fce5e33" +source = "git+https://github.com/storytold/craft-libs?rev=9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef#9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef" dependencies = [ "base64 0.22.1", "bumpalo", diff --git a/Cargo.toml b/Cargo.toml index 90297f29..65809315 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,3 +128,8 @@ codegen-units = 1 debug = false strip = true panic = "abort" + +# craft-libs' vendored krilla 0.8.2: decoded PNGs are capped at 1 GiB and reserved fallibly, and a +# panic while drawing unwinds without aborting. +[patch.crates-io] +krilla = { git = "https://github.com/storytold/craft-libs", rev = "9f75d3dc19b4b514c4f3d4847ed1ec1b0e86b4ef" } diff --git a/crates/pdf/src/tests.rs b/crates/pdf/src/tests.rs index 3656cf1e..a696e279 100644 --- a/crates/pdf/src/tests.rs +++ b/crates/pdf/src/tests.rs @@ -271,3 +271,30 @@ fn sample_files() { std::fs::write(format!("{stem}-{}.pdf", i + 1), export_pdf(&d, &[*f], &PdfOptions::default()).unwrap()).unwrap(); } } + +/// A tiny PNG whose header claims 30,000 × 30,000 RGBA (3.6 GB decoded) is refused when it is read, +/// before anything is decoded: the PDF writer is craft-libs' patched krilla. +#[test] +fn a_png_that_decodes_too_large_is_refused() { + fn chunk(out: &mut Vec, kind: &[u8; 4], data: &[u8]) { + out.extend_from_slice(&u32::try_from(data.len()).unwrap().to_be_bytes()); + let mut crc = flate2::Crc::new(); + crc.update(kind); + crc.update(data); + out.extend_from_slice(kind); + out.extend_from_slice(data); + out.extend_from_slice(&crc.sum().to_be_bytes()); + } + let mut ihdr = Vec::new(); + ihdr.extend_from_slice(&30_000u32.to_be_bytes()); + ihdr.extend_from_slice(&30_000u32.to_be_bytes()); + ihdr.extend_from_slice(&[8, 6, 0, 0, 0]); + let mut z = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default()); + std::io::Write::write_all(&mut z, &[0; 64]).unwrap(); + let mut png = b"\x89PNG\r\n\x1a\n".to_vec(); + chunk(&mut png, b"IHDR", &ihdr); + chunk(&mut png, b"IDAT", &z.finish().unwrap()); + chunk(&mut png, b"IEND", &[]); + const { assert!(krilla::CRAFT_UNWIND_SAFE_DROP) }; + assert!(krilla::image::Image::from_png(krilla::Data::from(png), true).is_err()); +}