From 7ab25755055f970e218e2c29920a952baef9f083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20Mittelst=C3=A4dt?= <62167861+TheGalitube@users.noreply.github.com> Date: Wed, 28 Oct 2026 16:41:59 +0000 Subject: [PATCH] Reject ZIP entries with inconsistent decoded sizes Found while testing crates/geocode/src/unzip.rs:64 Co-Authored-By: GPT-6-Luna --- crates/geocode/src/unzip.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/geocode/src/unzip.rs b/crates/geocode/src/unzip.rs index f78648d83..d422297a7 100644 --- a/crates/geocode/src/unzip.rs +++ b/crates/geocode/src/unzip.rs @@ -61,7 +61,13 @@ pub fn extract(zip: &[u8], name: &str, max: usize) -> Result, GeocodeErr return match method { 0 if data.len() <= max => Ok(data.to_vec()), 0 => Err(GeocodeError::Invalid(format!("{name} is larger than {max} bytes"))), - 8 => miniz_oxide::inflate::decompress_to_vec_with_limit(data, max).map_err(|e| bad(&format!("deflate: {e:?}"))), + 8 => { + let decoded = miniz_oxide::inflate::decompress_to_vec_with_limit(data, max).map_err(|e| bad(&format!("deflate: {e:?}")))?; + if decoded.len() != usize_ { + return Err(bad("uncompressed size")); + } + Ok(decoded) + } m => Err(bad(&format!("compression method {m}"))), }; } @@ -120,4 +126,15 @@ mod tests { let _ = extract(&bad, "cities15000.txt", 100); } } + + #[test] + fn rejects_deflated_entry_with_inconsistent_directory_size() { + let mut z = make("cities15000.txt", b"hello world"); + let eocd = z.len() - 22; + let cd = u32::from_le_bytes([z[eocd + 16], z[eocd + 17], z[eocd + 18], z[eocd + 19]]) as usize; + let declared_size = cd + 24; + z[declared_size..declared_size + 4].copy_from_slice(&2u32.to_le_bytes()); + + assert!(extract(&z, "cities15000.txt", 100).is_err()); + } }