fix: refuse numeric types with unusually many unused bits in v1 headers
libhdf5 1.14.4+ treats an integer, float or bit field wider than a byte whose precision and offset leave more than half its bits unused as corruption when the type sits in a header without a checksum (version 1), unless the file is opened with H5Pset_relax_file_integrity_checks (H5T_is_numeric_with_unusual_unused_bits). clawhdf5 read such types, e.g. a 3-bit integer in 4 bytes (cve-2024-29162) or a 32-bit float in 65525 bytes (cve-2024-32614, tmisc38a.h5). New Datatype::check_unused_bits (recursive) and Datatype::parse_in_header, which applies it for version-1 headers. Dataset datatypes (facade File, LazyFile, MmapFile; clawhdf5-io VOL, MPI VOL, async reader; the conformance probe) and compact attributes in version-1 headers use it. Conformance (cached corpus, tank): 570 ok, unchanged; cve-2024-29162, cve-2024-32614 and tmisc38a.h5 now refuse the object h5py refuses, and tmisc38b.h5 / unknown-1.h5 now fail with libhdf5's reason. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -362,6 +362,18 @@ fn extract_name(bytes: &[u8]) -> String {
|
||||
String::from_utf8_lossy(&bytes[..end]).into_owned()
|
||||
}
|
||||
|
||||
/// An attribute's datatype gets libhdf5's extra check for a header without
|
||||
/// a checksum (see [`Datatype::check_unused_bits`]).
|
||||
fn check_in_header(
|
||||
attr: AttributeMessage,
|
||||
header: &ObjectHeader,
|
||||
) -> Result<AttributeMessage, FormatError> {
|
||||
if header.version == 1 {
|
||||
attr.datatype.check_unused_bits()?;
|
||||
}
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
/// Extract all attribute messages from an object header.
|
||||
pub fn extract_attributes(
|
||||
header: &ObjectHeader,
|
||||
@@ -371,7 +383,7 @@ pub fn extract_attributes(
|
||||
for msg in &header.messages {
|
||||
if msg.msg_type == MessageType::Attribute {
|
||||
let attr = AttributeMessage::parse(&msg.data, length_size)?;
|
||||
attrs.push(attr);
|
||||
attrs.push(check_in_header(attr, header)?);
|
||||
}
|
||||
}
|
||||
Ok(attrs)
|
||||
@@ -465,6 +477,7 @@ fn extract_attributes_with(
|
||||
} else {
|
||||
AttributeMessage::parse_in_file(&msg.data, file_data, offset_size, length_size)
|
||||
};
|
||||
let attr = attr.and_then(|a| check_in_header(a, header));
|
||||
match attr {
|
||||
Ok(attr) => attrs.push(attr),
|
||||
Err(e) => on_error(e)?,
|
||||
|
||||
Reference in New Issue
Block a user