clawhdf5-remote: no overflow on lengths near u64::MAX
A server can claim any length in Content-Range. block_len computed start + block_size, which overflowed in the last blocks of a file claimed to be near u64::MAX (a panic in debug builds, a wrapped value in release); insert() multiplied block indices unchecked. The cache's block arithmetic is now saturating/checked, and a run that does not split into whole blocks is an error instead of an endless loop or a slice panic. The test server gains fake_total (claim a length, serve zeros past the data); a test reads the last bytes of such files and opens a file whose superblock EOF and root addresses sit near u64::MAX. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -544,3 +544,36 @@ fn requests_for_other_paths_are_not_counted() {
|
||||
assert_eq!(server.requests(), 1, "only the open counts");
|
||||
assert_eq!(server.log().len(), 1);
|
||||
}
|
||||
|
||||
/// A server claiming a length near `u64::MAX` (and serving zeros past the
|
||||
/// real data), with a file whose addresses point at the end of that range:
|
||||
/// clean errors or zeros, never an arithmetic overflow (a panic in debug).
|
||||
#[test]
|
||||
fn a_server_claiming_a_huge_length_does_not_overflow() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let mut bytes =
|
||||
std::fs::read(root.join("../clawhdf5-format/tests/fixtures/legacy/h5ex_g_iterate.h5"))
|
||||
.unwrap();
|
||||
// Superblock v0: end-of-file address, then the root symbol table
|
||||
// entry's object header, B-tree and heap addresses.
|
||||
bytes[0x28..0x30].copy_from_slice(&(u64::MAX - 1).to_le_bytes());
|
||||
for at in [0x40, 0x50, 0x58] {
|
||||
bytes[at..at + 8].copy_from_slice(&0xFFFF_FFFF_FFFF_F000u64.to_le_bytes());
|
||||
}
|
||||
let server = Server::start(vec![("/h.h5".into(), bytes)]);
|
||||
for total in [u64::MAX, u64::MAX - 1, 1 << 62] {
|
||||
server.shared.fake_total.store(total, Ordering::SeqCst);
|
||||
let url = server.url("/h.h5");
|
||||
let storage = storage_for_url(&url, &quick()).unwrap();
|
||||
assert_eq!(clawhdf5_format::storage::Storage::len(&*storage), total);
|
||||
for (off, n) in [(total - 100, 50), (total - 10, 100), (total - 1, 1)] {
|
||||
let got = clawhdf5_format::storage::Storage::read_at(&*storage, off, n).unwrap();
|
||||
assert_eq!(got.len() as u64, (total - off).min(n as u64));
|
||||
assert!(got.iter().all(|&b| b == 0));
|
||||
}
|
||||
if let Ok(f) = File::open_storage(storage) {
|
||||
let _ = transcript(&f);
|
||||
}
|
||||
let _ = open_url_with(&url, &quick()).map(|f| transcript(&f));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user