forked from 1b-agent/opencadstudio-upstream
fix(underlay): bound how far DWF packages and DGN streams inflate
The DWF package reader, the DGN stream inflater and the new DGN ExtendedColors scan each read a zlib/zip payload to its end, so a crafted or corrupt file could inflate until memory ran out. Cap them: 1 GiB for a whole DWF package, 512 MiB per DGN stream, 4 MiB per ExtendedColors record candidate - far above real files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d9899be35e
commit
0c1676e612
2 files changed
+21
-4
No files matched your search
@@ -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<u8> {
|
||||
// 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<u16> = out.chunks_exact(2).map(|c| u16::from_le_bytes([c[0], c[1]])).collect();
|
||||
|
||||
@@ -14,11 +14,15 @@ fn unzip(bytes: &[u8]) -> Option<HashMap<String, Vec<u8>>> {
|
||||
// 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)
|
||||
|
||||
Reference in new issue
Block a user