feat(format): ObjectHeader::object_class, libhdf5's object classification
libhdf5 decides what an object header is in a fixed order (H5O__obj_class_real): a group if it has a Symbol Table or Link Info message, a dataset if it has a Datatype *and* a Dataspace message, a named datatype if it has a Datatype message. The conformance probe called any header with a Data Layout message a dataset, so cve-2024-33874's /Dset1 (a datatype and a layout, no dataspace), which h5py opens as a named datatype, was reported as a dataset we failed to read (MissingMessage(Dataspace)). The probe now classifies with object_class(). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -31,7 +31,7 @@ use clawhdf5_format::filter_pipeline::FilterPipeline;
|
||||
use clawhdf5_format::group_v1::{self, GroupEntry};
|
||||
use clawhdf5_format::group_v2;
|
||||
use clawhdf5_format::message_type::MessageType;
|
||||
use clawhdf5_format::object_header::ObjectHeader;
|
||||
use clawhdf5_format::object_header::{ObjectClass, ObjectHeader};
|
||||
use clawhdf5_format::signature;
|
||||
use clawhdf5_format::superblock::Superblock;
|
||||
use clawhdf5_format::symbol_table::SymbolTableMessage;
|
||||
@@ -657,6 +657,20 @@ fn is_group(h: &ObjectHeader) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
/// The probe's kind for an object header: libhdf5's object class
|
||||
/// ([`ObjectHeader::object_class`]: group, then dataset — a datatype *and* a
|
||||
/// dataspace — then named datatype), which is what h5py opens the object as.
|
||||
/// The root group, and a header with only link messages, count as groups.
|
||||
fn kind_of(h: &ObjectHeader, is_root: bool) -> &'static str {
|
||||
match h.object_class() {
|
||||
Some(ObjectClass::Group) => "group",
|
||||
Some(ObjectClass::Dataset) => "dataset",
|
||||
_ if is_root || is_group(h) => "group",
|
||||
Some(ObjectClass::NamedDatatype) => "datatype",
|
||||
None => "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
install_hook();
|
||||
let path = std::env::args().nth(1).expect("usage: probe <file>");
|
||||
@@ -735,23 +749,7 @@ fn main() {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let is_ds = h
|
||||
.messages
|
||||
.iter()
|
||||
.any(|m| m.msg_type == MessageType::DataLayout);
|
||||
let kind = if is_ds {
|
||||
"dataset"
|
||||
} else if is_group(&h) || addr == sb.root_group_address {
|
||||
"group"
|
||||
} else if h
|
||||
.messages
|
||||
.iter()
|
||||
.any(|m| m.msg_type == MessageType::Datatype)
|
||||
{
|
||||
"datatype"
|
||||
} else {
|
||||
"unknown"
|
||||
};
|
||||
let kind = kind_of(&h, addr == sb.root_group_address);
|
||||
rec.insert("kind".into(), Value::String(kind.into()));
|
||||
if kind == "dataset"
|
||||
&& let Err(msg) = guarded(|| ctx.read_dataset(&h, &mut rec))
|
||||
@@ -867,6 +865,42 @@ mod tests {
|
||||
assert!(ieee_layout(&f32le));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kind_follows_libhdf5_object_class() {
|
||||
use clawhdf5_format::object_header::HeaderMessage;
|
||||
let header = |types: &[MessageType]| ObjectHeader {
|
||||
version: 2,
|
||||
messages: types
|
||||
.iter()
|
||||
.map(|&msg_type| HeaderMessage {
|
||||
msg_type,
|
||||
size: 0,
|
||||
flags: 0,
|
||||
creation_order: None,
|
||||
data: Vec::new(),
|
||||
})
|
||||
.collect(),
|
||||
reference_count: None,
|
||||
flags: 0,
|
||||
access_time: None,
|
||||
modification_time: None,
|
||||
change_time: None,
|
||||
birth_time: None,
|
||||
};
|
||||
use MessageType::*;
|
||||
// cve-2024-33874 `/Dset1`: a datatype and a layout but no dataspace
|
||||
// is a named datatype to libhdf5 (h5py opens it as one).
|
||||
assert_eq!(kind_of(&header(&[Datatype, DataLayout]), false), "datatype");
|
||||
assert_eq!(
|
||||
kind_of(&header(&[Datatype, Dataspace, DataLayout]), false),
|
||||
"dataset"
|
||||
);
|
||||
assert_eq!(kind_of(&header(&[SymbolTable]), false), "group");
|
||||
assert_eq!(kind_of(&header(&[Link]), false), "group");
|
||||
assert_eq!(kind_of(&header(&[]), true), "group");
|
||||
assert_eq!(kind_of(&header(&[]), false), "unknown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partial_precision_int_is_shifted_and_sign_extended() {
|
||||
let dt = Datatype::FixedPoint {
|
||||
|
||||
Reference in New Issue
Block a user