clawhdf5-remote, h5rs: never allocate a length the server only claims
h5rs check URL read the whole file with one read_at(0, len), len being whatever Content-Range said. BlockCache listed every block index of the span and preallocated len bytes: a server claiming 2^62 bytes for a 10 KB file made h5rs abort (memory allocation of 35184372088832 bytes failed). - BlockCache: a read spanning more than the budget (or eight max_requests) is fetched piece by piece and not kept, its output growing only as data arrives; read_ranges falls back to that per range; prefetch is clamped to the budget. - New clawhdf5_remote::download(storage, max_bytes): refuses a claimed length above the limit (RemoteError::TooLarge) before any request, then reads in 64 MiB steps. New RemoteError::Backend for read errors. - h5rs check downloads through it, with --max-download N (default 1 GiB). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -577,3 +577,30 @@ fn a_server_claiming_a_huge_length_does_not_overflow() {
|
||||
let _ = open_url_with(&url, &quick()).map(|f| transcript(&f));
|
||||
}
|
||||
}
|
||||
|
||||
/// `download` reads a whole file in bounded steps, and refuses a claimed
|
||||
/// length beyond its limit before reading anything.
|
||||
#[test]
|
||||
fn download_is_bounded_by_its_limit_not_the_claimed_length() {
|
||||
let bytes = multi_block_file();
|
||||
let server = Server::start(vec![("/m.h5".into(), bytes.clone())]);
|
||||
let url = server.url("/m.h5");
|
||||
let storage = storage_for_url(&url, &quick()).unwrap();
|
||||
let got = clawhdf5_remote::download(&*storage, clawhdf5_remote::DEFAULT_MAX_DOWNLOAD).unwrap();
|
||||
assert_eq!(got, bytes);
|
||||
let e = clawhdf5_remote::download(&*storage, 1000).unwrap_err();
|
||||
assert!(
|
||||
matches!(e, Error::Remote(RemoteError::TooLarge { limit: 1000, .. })),
|
||||
"{e}"
|
||||
);
|
||||
server.shared.fake_total.store(1 << 62, Ordering::SeqCst);
|
||||
let storage = storage_for_url(&url, &quick()).unwrap();
|
||||
server.reset();
|
||||
let e =
|
||||
clawhdf5_remote::download(&*storage, clawhdf5_remote::DEFAULT_MAX_DOWNLOAD).unwrap_err();
|
||||
assert!(
|
||||
matches!(e, Error::Remote(RemoteError::TooLarge { len, .. }) if len == 1 << 62),
|
||||
"{e}"
|
||||
);
|
||||
assert_eq!(server.requests(), 0, "refused before reading");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user