format: no truncating u64 -> usize casts

Every `u64 as usize` cast in clawhdf5-format (115 on wasm32) now goes
through addr::to_usize for values read from the file — addresses, lengths,
counts, dimensions: FormatError::Overflow where the value does not fit
instead of wrapping onto another part of the file on a 32-bit target — or
addr::saturating_usize for counts bounded by something in memory (codec
progress counters, writer sizes), which fail a bounds check or allocation
rather than wrap. A chunk whose offset does not fit lies outside the
dataset and is skipped; partial reads treat such an offset as out of the
buffers. On 64-bit targets nothing changes.

scripts/check-32bit-casts.sh (run by ci-test.sh) lints the wasm32 build
with clippy's cast_possible_truncation and fails on any u64 -> usize
finding; before this commit it listed 115.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-26 13:33:24 -05:00
co-authored by Claude Opus 5.5
parent 02e89c1d2d
commit b41583113a
27 changed files with 236 additions and 99 deletions
+9 -8
View File
@@ -6,6 +6,7 @@ use alloc::{collections::BTreeMap, format, string::String, vec, vec::Vec};
#[cfg(feature = "std")]
use std::collections::BTreeMap;
use crate::addr::to_usize;
#[cfg(feature = "std")]
use crate::chunk_cache::ChunkCache;
use crate::chunked_read::read_chunked_data;
@@ -117,7 +118,7 @@ pub fn read_raw_data_zerocopy<'a>(
dataspace: &Dataspace,
datatype: &Datatype,
) -> Result<Option<&'a [u8]>, FormatError> {
let num_elements = dataspace.num_elements() as usize;
let num_elements = to_usize(dataspace.num_elements())?;
let elem_size = datatype.type_size() as usize;
let expected_size = num_elements.checked_mul(elem_size).ok_or_else(|| {
FormatError::Overflow(format!(
@@ -128,7 +129,7 @@ pub fn read_raw_data_zerocopy<'a>(
match layout {
DataLayout::Contiguous { address, size } => {
let addr = address.ok_or(FormatError::NoDataAllocated)?;
let addr = addr as usize;
let addr = to_usize(addr)?;
let sz = contiguous_read_len(*size, expected_size)?;
ensure_len(file_data, addr, sz)?;
Ok(Some(&file_data[addr..addr + sz]))
@@ -219,7 +220,7 @@ fn read_raw_data_full_impl(
length_size: u8,
resolver: Option<&VdsSourceResolver>,
) -> Result<Vec<u8>, FormatError> {
let num_elements = dataspace.num_elements() as usize;
let num_elements = to_usize(dataspace.num_elements())?;
let elem_size = datatype.type_size() as usize;
let expected_size = num_elements.checked_mul(elem_size).ok_or_else(|| {
FormatError::Overflow(format!(
@@ -239,7 +240,7 @@ fn read_raw_data_full_impl(
}
DataLayout::Contiguous { address, size } => {
let addr = address.ok_or(FormatError::NoDataAllocated)?;
let addr = addr as usize;
let addr = to_usize(addr)?;
let sz = contiguous_read_len(*size, expected_size)?;
ensure_len(file_data, addr, sz)?;
let mut out = crate::bulk_alloc::vec_for_bulk(sz);
@@ -582,7 +583,7 @@ pub fn extract_selection_from_buffer(
let rank = dims.len();
let mut ds_strides = vec![1usize; rank];
for i in (0..rank.saturating_sub(1)).rev() {
ds_strides[i] = ds_strides[i + 1] * dims[i + 1] as usize;
ds_strides[i] = ds_strides[i + 1] * to_usize(dims[i + 1])?;
}
let mut output = Vec::with_capacity(pts.len() * elem_size);
@@ -590,8 +591,8 @@ pub fn extract_selection_from_buffer(
let flat: usize = pt
.iter()
.zip(ds_strides.iter())
.map(|(&p, &s)| p as usize * s)
.sum();
.map(|(&p, &s)| Ok(to_usize(p)? * s))
.sum::<Result<usize, FormatError>>()?;
let src = flat * elem_size;
if src + elem_size <= full_data.len() {
output.extend_from_slice(&full_data[src..src + elem_size]);
@@ -1341,7 +1342,7 @@ pub fn read_compound_fields(
let mut fields = Vec::with_capacity(members.len());
for m in members {
let field_size = m.datatype.type_size() as usize;
let offset = m.byte_offset as usize;
let offset = to_usize(m.byte_offset)?;
if offset
.checked_add(field_size)
.is_none_or(|end| end > elem_size)