forked from 1b-agent/opencadstudio-upstream
fix(underlay): malformed DWF and DGN files no longer crash or hang the reader
- W2D: a stream cut off right after an extended opcode's '(' sliced its
body backwards and panicked.
- DGN V8: the compound-file header was indexed before its length was
known (a short file with the signature panicked), the sector shift was
taken on trust (0 or 1 underflowed `sector / 4 - 1` into an endless
loop), and the DIFAT chain count was unbounded. The header must now be
complete, the shift 9 or 12, and the chain no longer than the file.
Found fuzzing the #1514 readers with truncated and random input.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
e7aee3baa9
commit
e26e0352b7
2 files changed
+14
-4
No files matched your search
@@ -39,16 +39,24 @@ fn u32_at(b: &[u8], at: usize) -> Option<u32> {
|
||||
|
||||
impl<'a> Cfb<'a> {
|
||||
fn open(b: &'a [u8]) -> Option<Self> {
|
||||
if !b.starts_with(&[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]) {
|
||||
// The 512-byte header, with the only two sector sizes the format
|
||||
// has (512 and 4096 bytes); anything else is not a compound file.
|
||||
if b.len() < 512 || !b.starts_with(&[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]) {
|
||||
return None;
|
||||
}
|
||||
let sector = 1usize << u16::from_le_bytes([b[30], b[31]]);
|
||||
let sector_shift = u16::from_le_bytes([b[30], b[31]]);
|
||||
if sector_shift != 9 && sector_shift != 12 {
|
||||
return None;
|
||||
}
|
||||
let sector = 1usize << sector_shift;
|
||||
let mini_sector_shift = u16::from_le_bytes([b[32], b[33]]);
|
||||
if mini_sector_shift != 6 {
|
||||
return None;
|
||||
}
|
||||
let mut difat: Vec<u32> = (0..109).filter_map(|i| u32_at(b, 76 + i * 4)).filter(|&v| v < END).collect();
|
||||
let (mut next, mut count) = (u32_at(b, 68)?, u32_at(b, 72)?);
|
||||
// The DIFAT chain cannot hold more sectors than the file does, so a
|
||||
// looping chain stops there.
|
||||
let (mut next, mut count) = (u32_at(b, 68)?, u32_at(b, 72)?.min((b.len() / sector) as u32));
|
||||
while count > 0 && next < END {
|
||||
let at = (next as usize + 1) * sector;
|
||||
for i in 0..sector / 4 - 1 {
|
||||
|
||||
@@ -270,7 +270,9 @@ mod w2d {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let body = &self.b[start + 1..self.i.saturating_sub(1)];
|
||||
// A stream cut off right after its '(' has no body at all.
|
||||
let end = self.i.saturating_sub(1).max(start + 1);
|
||||
let body = self.b.get(start + 1..end)?;
|
||||
let name_len = body.iter().position(|c| c.is_ascii_whitespace() || *c == b'(').unwrap_or(body.len());
|
||||
Some((String::from_utf8_lossy(&body[..name_len]).to_string(), &body[name_len..]))
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user