diff --git a/crates/clawhdf5-format/src/object_header.rs b/crates/clawhdf5-format/src/object_header.rs index b28fdad..2c3c819 100644 --- a/crates/clawhdf5-format/src/object_header.rs +++ b/crates/clawhdf5-format/src/object_header.rs @@ -421,6 +421,13 @@ impl ObjectHeader { }; pos += msg_header_size; + // `end` is where the messages stop and the checksum starts. + // libhdf5 bounds a message by the chunk including its checksum, + // but a message that runs into the checksum still fails there: + // its loop stops at the checksum, and reading the checksum from + // past its start overruns the chunk ("ran off end of input + // buffer while decoding"). Both refuse it; only the text + // differs. if msg_data_size > end - pos { return Err(FormatError::InvalidObjectHeader( "message size exceeds buffer end", diff --git a/crates/clawhdf5/tests/header_validation_interop.rs b/crates/clawhdf5/tests/header_validation_interop.rs index 7cdd4b4..2f52d27 100644 --- a/crates/clawhdf5/tests/header_validation_interop.rs +++ b/crates/clawhdf5/tests/header_validation_interop.rs @@ -252,6 +252,54 @@ for libver in ("earliest", "latest"): } } +/// A v2 object header message whose size runs past the messages into the +/// chunk's checksum is refused by libhdf5 whether it runs 1 byte or more +/// into the checksum (its loop stops at the checksum, and then the checksum +/// read and the size check fail), so it is refused here too. +#[test] +fn v2_header_message_running_into_the_checksum_is_refused() { + skip_if_no_python!(); + let dir = tempfile::tempdir().unwrap(); + let body = format!( + "{FIX_OHDR_PY}{}", + r#" +good = os.path.join(d, "good.h5") +with h5py.File(good, "w", libver="latest") as f: + f.create_dataset("d", data=np.arange(4, dtype="