diff --git a/src/scene/model/underlay_vector/dgn8.rs b/src/scene/model/underlay_vector/dgn8.rs index 35315a04e..e1cc34d91 100644 --- a/src/scene/model/underlay_vector/dgn8.rs +++ b/src/scene/model/underlay_vector/dgn8.rs @@ -157,10 +157,15 @@ impl<'a> Cfb<'a> { /// A DGN stream: raw, or zlib data after a 16-byte header. fn inflate(data: &[u8]) -> Vec { + // Far above any real element stream; stops a crafted one from + // inflating until memory runs out. + const STREAM_LIMIT: u64 = 512 << 20; for skip in [16usize, 0] { if let Some(z) = data.get(skip..) { let mut out = Vec::new(); - if flate2::read::ZlibDecoder::new(z).read_to_end(&mut out).is_ok() && !out.is_empty() { + if flate2::read::ZlibDecoder::new(z).take(STREAM_LIMIT).read_to_end(&mut out).is_ok() + && !out.is_empty() + { return out; } } @@ -237,8 +242,16 @@ fn extended_colors(cfb: &Cfb) -> Vec<[u8; 3]> { if a & 0x0f != 8 || ((a as u16) << 8 | b as u16) % 31 != 0 { continue; } + // The record is a short XML list; the cap keeps a stream that + // inflates without end (or a crafted one) from exhausting memory. + const RECORD_LIMIT: u64 = 4 << 20; let mut out = Vec::new(); - if flate2::read::ZlibDecoder::new(&attrs[at..]).read_to_end(&mut out).is_err() || out.len() < 2 { + if flate2::read::ZlibDecoder::new(&attrs[at..]) + .take(RECORD_LIMIT) + .read_to_end(&mut out) + .is_err() + || out.len() < 2 + { continue; } let units: Vec = out.chunks_exact(2).map(|c| u16::from_le_bytes([c[0], c[1]])).collect(); diff --git a/src/scene/model/underlay_vector/dwf.rs b/src/scene/model/underlay_vector/dwf.rs index b4b7aa3f8..52e26b16d 100644 --- a/src/scene/model/underlay_vector/dwf.rs +++ b/src/scene/model/underlay_vector/dwf.rs @@ -14,11 +14,15 @@ fn unzip(bytes: &[u8]) -> Option>> { // the offset itself. let mut archive = zip::ZipArchive::new(Cursor::new(bytes)).ok()?; let mut files = HashMap::new(); + // Total inflated size a package may reach: far above any real sheet + // set, and a crafted archive cannot exhaust memory. + let mut budget: u64 = 1 << 30; for i in 0..archive.len() { - let mut file = archive.by_index(i).ok()?; + let file = archive.by_index(i).ok()?; let name = file.name().replace('\\', "/").trim_start_matches('/').to_string(); let mut data = Vec::new(); - file.read_to_end(&mut data).ok()?; + file.take(budget + 1).read_to_end(&mut data).ok()?; + budget = budget.checked_sub(data.len() as u64)?; files.insert(name, data); } Some(files)