fix(format): overflow-checked sizes and fallible allocation on chunked reads
Dataspace and chunk dimensions are untrusted 64-bit fields, but the chunked read paths computed `num_elements() as usize * elem_size` and `chunk_dims.product() * elem_size` with plain arithmetic and fed the result to `vec![0u8; n]`. A crafted file could wrap the product (under-sizing the output buffer that chunks are then copied into) or request an allocation large enough to abort the process. - Dataspace::checked_num_elements, checked_byte_len, checked_chunk_byte_len and alloc_output (try_reserve_exact) replace the plain products and vec![0; n] at every chunked read site, plus the VDS and hyperslab paths. Overflow and allocation failure are FormatError::Overflow. - Dataspace::num_elements saturates instead of wrapping. - A zero-element dataset returns early, which also keeps the stride products in range when another dimension is huge. - parallel_read.rs: the three `c_addr + size > len` bounds checks used a raw add; they now use checked_add like the rest of the crate. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
3ed0489faa
commit
6e84f31ed6
@@ -73,9 +73,12 @@ pub fn decompress_chunks_lane_partitioned(
|
||||
let c_addr = chunk_info.address as usize;
|
||||
let size = chunk_info.chunk_size as usize;
|
||||
|
||||
if c_addr + size > file_data.len() {
|
||||
if c_addr
|
||||
.checked_add(size)
|
||||
.is_none_or(|end| end > file_data.len())
|
||||
{
|
||||
return Err(FormatError::UnexpectedEof {
|
||||
expected: c_addr + size,
|
||||
expected: c_addr.saturating_add(size),
|
||||
available: file_data.len(),
|
||||
});
|
||||
}
|
||||
@@ -144,9 +147,12 @@ pub fn decompress_chunks_parallel(
|
||||
.map(|(index, chunk_info)| {
|
||||
let c_addr = chunk_info.address as usize;
|
||||
let size = chunk_info.chunk_size as usize;
|
||||
if c_addr + size > file_data.len() {
|
||||
if c_addr
|
||||
.checked_add(size)
|
||||
.is_none_or(|end| end > file_data.len())
|
||||
{
|
||||
return Err(FormatError::UnexpectedEof {
|
||||
expected: c_addr + size,
|
||||
expected: c_addr.saturating_add(size),
|
||||
available: file_data.len(),
|
||||
});
|
||||
}
|
||||
@@ -182,9 +188,12 @@ pub fn decompress_chunks_sequential(
|
||||
for chunk_info in chunks {
|
||||
let c_addr = chunk_info.address as usize;
|
||||
let size = chunk_info.chunk_size as usize;
|
||||
if c_addr + size > file_data.len() {
|
||||
if c_addr
|
||||
.checked_add(size)
|
||||
.is_none_or(|end| end > file_data.len())
|
||||
{
|
||||
return Err(FormatError::UnexpectedEof {
|
||||
expected: c_addr + size,
|
||||
expected: c_addr.saturating_add(size),
|
||||
available: file_data.len(),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user