fix(format): a Blosc frame shorter than its header is an error, not a panic
A hostile chunk whose header gave a compressed size below 16 bytes, not stored raw, made the block-table check subtract past zero: a panic in any build with overflow checks (cargo test, maturin develop, debug CLI). The frame size is now checked against the header size, and the stream-length read no longer adds to an untrusted offset. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -143,6 +143,9 @@ pub fn blosc_decompress(input: &[u8], limit: usize) -> Result<Vec<u8>, FormatErr
|
||||
if cbytes > input.len() {
|
||||
return Err(err("frame is longer than the chunk"));
|
||||
}
|
||||
if cbytes < HEADER {
|
||||
return Err(err("truncated frame"));
|
||||
}
|
||||
let src = &input[..cbytes];
|
||||
if nbytes == 0 {
|
||||
return Ok(Vec::new());
|
||||
@@ -189,10 +192,11 @@ pub fn blosc_decompress(input: &[u8], limit: usize) -> Result<Vec<u8>, FormatErr
|
||||
let mut pos = le32(src, HEADER + 4 * j)?;
|
||||
let tmp = &mut tmp[..bsize];
|
||||
for s in 0..nsplits {
|
||||
if pos + 4 > src.len() {
|
||||
return Err(err("block offset out of range"));
|
||||
}
|
||||
let clen = le32(src, pos)?;
|
||||
let clen = src
|
||||
.get(pos..)
|
||||
.and_then(|rest| rest.get(..4))
|
||||
.map(|b| u32::from_le_bytes(b.try_into().unwrap()) as usize)
|
||||
.ok_or_else(|| err("block offset out of range"))?;
|
||||
pos += 4;
|
||||
let stream = src
|
||||
.get(pos..pos.saturating_add(clen))
|
||||
@@ -601,4 +605,21 @@ mod tests {
|
||||
let ctx0 = FilterContext { filter: &f0, ..ctx };
|
||||
assert!(blosc_encode(&data, &ctx0).is_err());
|
||||
}
|
||||
|
||||
/// A frame whose header claims a compressed size smaller than the
|
||||
/// header itself, not stored raw: an error, not an arithmetic overflow
|
||||
/// (it panicked in debug builds).
|
||||
#[test]
|
||||
fn frame_size_below_the_header_is_an_error() {
|
||||
let mut frame = vec![2u8, 1, 1 << 5, 4];
|
||||
for v in [64u32, 64, 8] {
|
||||
frame.extend_from_slice(&v.to_le_bytes());
|
||||
}
|
||||
frame.extend_from_slice(&[0; 40]);
|
||||
assert!(blosc_decompress(&frame, 1000).is_err());
|
||||
for cbytes in 0..16u32 {
|
||||
frame[12..16].copy_from_slice(&cbytes.to_le_bytes());
|
||||
assert!(blosc_decompress(&frame, 1000).is_err(), "cbytes={cbytes}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user