diff --git a/crates/format/src/lib.rs b/crates/format/src/lib.rs index 8f9d8856e..a03667209 100644 --- a/crates/format/src/lib.rs +++ b/crates/format/src/lib.rs @@ -224,10 +224,17 @@ fn read_entry( return Err(too_big()); } let mut v = Vec::new(); - if f.take(limit.saturating_add(1)).read_to_end(&mut v).is_err() { + let read = f.take(limit.saturating_add(1)).read_to_end(&mut v); + let n = v.len() as u64; + // What a failed read produced was inflated all the same: it counts against the archive's budget. + if read.is_err() { + if stored { + *stored_left = stored_left.saturating_sub(n); + } else { + *left = left.saturating_sub(n); + } return Ok(None); } - let n = v.len() as u64; if n > limit { return Err(too_big()); } diff --git a/crates/format/src/tests.rs b/crates/format/src/tests.rs index ca57c7770..0e6f613cd 100644 --- a/crates/format/src/tests.rs +++ b/crates/format/src/tests.rs @@ -3,6 +3,25 @@ use designcraft_doc::build::NewDocument; use designcraft_doc::geom::Rect; use designcraft_doc::{Asset, ParaFormat, SpreadRef}; +/// An entry that fails after inflating (a bad checksum) still uses up the archive's byte budget. +#[test] +fn a_failed_read_is_charged_to_the_budget() { + let mut z = zip::ZipWriter::new(Cursor::new(Vec::new())); + let stored = zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored); + z.start_file("a", stored).unwrap(); + z.write_all(&[7u8; 1000]).unwrap(); + let mut bytes = z.finish().unwrap().into_inner(); + // Flip a data byte: the entry reads in full, then fails its checksum. + // The payload is the only run of 1,000 consecutive 7s in the archive. + let start = bytes.windows(1000).position(|w| w.iter().all(|b| *b == 7)).expect("payload"); + bytes[start] = 8; + let mut archive = zip::ZipArchive::new(Cursor::new(bytes.as_slice())).unwrap(); + let (mut left, mut stored_left) = (5000, 5000); + assert_eq!(read_entry(&mut archive, "a", 4000, &mut left, &mut stored_left).unwrap(), None); + // A failed stored read is charged to the stored budget only. + assert_eq!((left, stored_left), (5000, 4000)); +} + fn png_asset(d: &mut Document, data: &[u8]) -> AssetId { let aid = AssetId(d.alloc()); let a =