Files
clawhdf5/crates/clawhdf5-format/src/error.rs
T
osobhandClaude Opus 5.5 16b7359485 fix(format): a v1 group with an empty link name fails its listing
libhdf5 refuses to list a symbol-table group that has an entry with an
empty name (H5G__ent_to_link: "invalid link name"), so h5py cannot list
cve-2021-46244's /BAG_root. We listed it, with an object at "/BAG_root/"
(the empty name, pointing at address 0). resolve_v1_group_entries — the
listing — now fails with the new FormatError::InvalidLinkName; path
lookups still find the group's other names, as libhdf5's by-name lookup
does.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-26 10:15:50 -05:00

509 lines
20 KiB
Rust

//! Error types for HDF5 format parsing.
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(feature = "std")]
use std::string::String;
use core::fmt;
/// Errors that can occur when parsing HDF5 binary format structures.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FormatError {
/// The HDF5 magic signature was not found at any valid offset.
SignatureNotFound,
/// The superblock version is not supported.
UnsupportedVersion(u8),
/// Unexpected end of data.
UnexpectedEof {
/// Number of bytes expected.
expected: usize,
/// Number of bytes actually available.
available: usize,
},
/// Invalid offset size (must be 2, 4, or 8).
InvalidOffsetSize(u8),
/// Invalid length size (must be 2, 4, or 8).
InvalidLengthSize(u8),
/// Invalid object header signature.
InvalidObjectHeaderSignature,
/// Invalid object header version.
InvalidObjectHeaderVersion(u8),
/// Unknown message type that is marked as must-understand.
UnsupportedMessage(u16),
/// Invalid datatype class.
InvalidDatatypeClass(u8),
/// Invalid datatype version for a given class.
InvalidDatatypeVersion {
/// The type class.
class: u8,
/// The version found.
version: u8,
},
/// Invalid string padding type.
InvalidStringPadding(u8),
/// Invalid character set.
InvalidCharacterSet(u8),
/// Invalid byte order.
InvalidByteOrder(u8),
/// Invalid reference type.
InvalidReferenceType(u8),
/// Invalid dataspace version.
InvalidDataspaceVersion(u8),
/// Invalid dataspace type.
InvalidDataspaceType(u8),
/// Invalid data layout version.
InvalidLayoutVersion(u8),
/// Invalid data layout class.
InvalidLayoutClass(u8),
/// No data allocated for contiguous layout.
NoDataAllocated,
/// Type mismatch when reading data.
TypeMismatch {
/// Expected type description.
expected: &'static str,
/// Actual type description.
actual: &'static str,
},
/// Data size mismatch.
DataSizeMismatch {
/// Expected size in bytes.
expected: usize,
/// Actual size in bytes.
actual: usize,
},
/// Invalid local heap signature.
InvalidLocalHeapSignature,
/// Invalid local heap version.
InvalidLocalHeapVersion(u8),
/// A local heap's free list points outside its data segment (libhdf5:
/// "bad heap free list").
InvalidLocalHeapFreeList,
/// Invalid B-tree v1 signature.
InvalidBTreeSignature,
/// Invalid B-tree node type.
InvalidBTreeNodeType(u8),
/// Invalid symbol table node signature.
InvalidSymbolTableNodeSignature,
/// Invalid symbol table node version.
InvalidSymbolTableNodeVersion(u8),
/// Path not found during group traversal.
PathNotFound(String),
/// Invalid Link message version.
InvalidLinkVersion(u8),
/// Invalid link type code.
InvalidLinkType(u8),
/// Invalid Link Info message version.
InvalidLinkInfoVersion(u8),
/// Invalid Group Info message version.
InvalidGroupInfoVersion(u8),
/// Invalid B-tree v2 signature.
InvalidBTreeV2Signature,
/// Invalid B-tree v2 version.
InvalidBTreeV2Version(u8),
/// Invalid fractal heap signature.
InvalidFractalHeapSignature,
/// Invalid fractal heap version.
InvalidFractalHeapVersion(u8),
/// Invalid heap ID type.
InvalidHeapIdType(u8),
/// Invalid attribute message version.
InvalidAttributeVersion(u8),
/// Invalid Attribute Info message version.
InvalidAttributeInfoVersion(u8),
/// Invalid shared message version.
InvalidSharedMessageVersion(u8),
/// A message is marked shared but was parsed without access to the file,
/// so the reference to the real message could not be followed.
UnresolvedSharedMessage,
/// A shared-message reference points at an object header that holds no
/// (unshared) message of the referenced type (raw message type id).
SharedMessageTargetMissing(u16),
/// A superblock was parsed at a non-zero offset of the buffer (the file
/// has a user block of this many bytes). HDF5 addresses are relative to
/// the superblock, so the buffer must start there: see
/// `signature::split_user_block`.
UserBlockNotStripped(u64),
/// A selection does not fit the dataset it was applied to (wrong rank, or
/// it reaches past a dimension's extent).
SelectionOutOfBounds(String),
/// The dataset's raw data is stored in external files (External Data
/// Files message), which this reader does not follow.
ExternalDataFilesUnsupported,
/// The path goes through an external link (a link into another file),
/// which this reader does not follow.
ExternalLinkUnsupported {
/// The file the link points into.
filename: String,
/// The object path within that file.
object_path: String,
},
/// Invalid SOHM table version.
InvalidSohmTableVersion(u8),
/// Invalid SOHM table signature (expected "SMTB").
InvalidSohmTableSignature,
/// Invalid SOHM list signature (expected "SMLI").
InvalidSohmListSignature,
/// Invalid global heap collection signature.
InvalidGlobalHeapSignature,
/// Invalid global heap version.
InvalidGlobalHeapVersion(u8),
/// Global heap object not found.
GlobalHeapObjectNotFound {
/// Address of the collection.
collection_address: u64,
/// Index that was not found.
index: u16,
},
/// Variable-length data error.
VlDataError(String),
/// Serialization error.
SerializationError(String),
/// Dataset is missing data.
DatasetMissingData,
/// Dataset is missing shape.
DatasetMissingShape,
/// Invalid filter pipeline version.
InvalidFilterPipelineVersion(u8),
/// Unsupported filter ID.
UnsupportedFilter(u16),
/// Filter processing error.
FilterError(String),
/// Decompression error.
DecompressionError(String),
/// Compression error.
CompressionError(String),
/// Fletcher32 checksum mismatch.
Fletcher32Mismatch {
/// Expected checksum.
expected: u32,
/// Computed checksum.
computed: u32,
},
/// Chunked dataset read error.
ChunkedReadError(String),
/// Chunk assembly error.
ChunkAssemblyError(String),
/// CRC32C checksum mismatch.
ChecksumMismatch {
/// The checksum stored in the file.
expected: u32,
/// The checksum we computed.
computed: u32,
},
/// Maximum nesting/continuation depth exceeded (malformed data protection).
NestingDepthExceeded,
/// Duplicate dataset name detected during parallel metadata merge.
DuplicateDatasetName(String),
/// Integer overflow in size computation (malformed data protection).
Overflow(String),
/// An object header that libhdf5 refuses to load (the reason is
/// libhdf5's own error text): a misaligned or overrunning message, a
/// wrong message count, contradictory message flags, a message of a
/// class that cannot be shared flagged shareable, …
InvalidObjectHeader(&'static str),
/// A datatype message libhdf5 refuses to decode (the reason is
/// libhdf5's own error text): size 0, bit fields outside the type,
/// an empty enum name, a compound member outside its compound, …
InvalidDatatype(String),
/// A chunked layout whose chunk dimensions libhdf5 refuses: a zero
/// dimension, a rank that does not match the dataspace, an element size
/// that is not the datatype's, or a chunk of 4 GiB or more indexed by a
/// version-1 B-tree.
InvalidChunkDimensions(String),
/// The superblock's end-of-file address lies past the end of the file:
/// the file was truncated (libhdf5 refuses to open it).
TruncatedFile {
/// End of file recorded in the superblock (relative to byte 0).
stored_eof: u64,
/// The file's actual length in bytes.
actual_len: u64,
},
/// A link libhdf5 refuses to list: a symbol-table entry with an empty
/// name ("invalid link name"). Listing the group fails, as in libhdf5.
InvalidLinkName,
}
impl fmt::Display for FormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatError::SignatureNotFound => {
write!(f, "HDF5 signature not found at any valid offset")
}
FormatError::UnsupportedVersion(v) => {
write!(f, "unsupported superblock version: {v}")
}
FormatError::UnexpectedEof {
expected,
available,
} => {
write!(f, "unexpected EOF: need {expected} bytes, have {available}")
}
FormatError::InvalidOffsetSize(s) => {
write!(f, "invalid offset size: {s} (must be 2, 4, or 8)")
}
FormatError::InvalidLengthSize(s) => {
write!(f, "invalid length size: {s} (must be 2, 4, or 8)")
}
FormatError::InvalidObjectHeaderSignature => {
write!(f, "invalid object header signature")
}
FormatError::InvalidObjectHeaderVersion(v) => {
write!(f, "invalid object header version: {v}")
}
FormatError::UnsupportedMessage(id) => {
write!(
f,
"unsupported message type {id:#06x} marked as must-understand"
)
}
FormatError::InvalidDatatypeClass(c) => {
write!(f, "invalid datatype class: {c}")
}
FormatError::InvalidDatatypeVersion { class, version } => {
write!(f, "invalid datatype version {version} for class {class}")
}
FormatError::InvalidStringPadding(p) => {
write!(f, "invalid string padding type: {p}")
}
FormatError::InvalidCharacterSet(c) => {
write!(f, "invalid character set: {c}")
}
FormatError::InvalidByteOrder(b) => {
write!(f, "invalid byte order: {b}")
}
FormatError::InvalidReferenceType(r) => {
write!(f, "invalid reference type: {r}")
}
FormatError::InvalidDataspaceVersion(v) => {
write!(f, "invalid dataspace version: {v}")
}
FormatError::InvalidDataspaceType(t) => {
write!(f, "invalid dataspace type: {t}")
}
FormatError::InvalidLayoutVersion(v) => {
write!(f, "invalid data layout version: {v}")
}
FormatError::InvalidLayoutClass(c) => {
write!(f, "invalid data layout class: {c}")
}
FormatError::NoDataAllocated => {
write!(f, "no data allocated for contiguous layout")
}
FormatError::TypeMismatch { expected, actual } => {
write!(f, "type mismatch: expected {expected}, got {actual}")
}
FormatError::DataSizeMismatch { expected, actual } => {
write!(
f,
"data size mismatch: expected {expected} bytes, got {actual} bytes"
)
}
FormatError::InvalidLocalHeapSignature => {
write!(f, "invalid local heap signature")
}
FormatError::InvalidLocalHeapFreeList => {
write!(f, "bad local heap free list")
}
FormatError::InvalidLocalHeapVersion(v) => {
write!(f, "invalid local heap version: {v}")
}
FormatError::InvalidBTreeSignature => {
write!(f, "invalid B-tree v1 signature")
}
FormatError::InvalidBTreeNodeType(t) => {
write!(f, "invalid B-tree node type: {t}")
}
FormatError::InvalidSymbolTableNodeSignature => {
write!(f, "invalid symbol table node signature")
}
FormatError::InvalidSymbolTableNodeVersion(v) => {
write!(f, "invalid symbol table node version: {v}")
}
FormatError::PathNotFound(p) => {
write!(f, "path not found: {p}")
}
FormatError::InvalidLinkVersion(v) => {
write!(f, "invalid link message version: {v}")
}
FormatError::InvalidLinkType(t) => {
write!(f, "invalid link type: {t}")
}
FormatError::InvalidLinkInfoVersion(v) => {
write!(f, "invalid link info message version: {v}")
}
FormatError::InvalidGroupInfoVersion(v) => {
write!(f, "invalid group info message version: {v}")
}
FormatError::InvalidBTreeV2Signature => {
write!(f, "invalid B-tree v2 signature")
}
FormatError::InvalidBTreeV2Version(v) => {
write!(f, "invalid B-tree v2 version: {v}")
}
FormatError::InvalidFractalHeapSignature => {
write!(f, "invalid fractal heap signature")
}
FormatError::InvalidFractalHeapVersion(v) => {
write!(f, "invalid fractal heap version: {v}")
}
FormatError::InvalidHeapIdType(t) => {
write!(f, "invalid heap ID type: {t}")
}
FormatError::InvalidAttributeVersion(v) => {
write!(f, "invalid attribute message version: {v}")
}
FormatError::InvalidAttributeInfoVersion(v) => {
write!(f, "invalid attribute info message version: {v}")
}
FormatError::InvalidSharedMessageVersion(v) => {
write!(f, "invalid shared message version: {v}")
}
FormatError::ExternalLinkUnsupported {
filename,
object_path,
} => write!(
f,
"path goes through an external link to {object_path} in {filename}, which is \
not supported"
),
FormatError::ExternalDataFilesUnsupported => write!(
f,
"dataset raw data is stored in external file(s), which is not supported"
),
FormatError::SelectionOutOfBounds(msg) => {
write!(f, "selection out of bounds: {msg}")
}
FormatError::UserBlockNotStripped(n) => write!(
f,
"file has a {n}-byte user block: parse the bytes from the superblock on \
(signature::split_user_block)"
),
FormatError::SharedMessageTargetMissing(t) => write!(
f,
"shared message reference points at an object header with no message of type \
{t:#06x}"
),
FormatError::UnresolvedSharedMessage => write!(
f,
"message is shared but no file data was available to resolve it"
),
FormatError::InvalidSohmTableVersion(v) => {
write!(f, "invalid SOHM table version: {v}")
}
FormatError::InvalidSohmTableSignature => {
write!(f, "invalid SOHM table signature (expected SMTB)")
}
FormatError::InvalidSohmListSignature => {
write!(f, "invalid SOHM list signature (expected SMLI)")
}
FormatError::InvalidGlobalHeapSignature => {
write!(f, "invalid global heap collection signature")
}
FormatError::InvalidGlobalHeapVersion(v) => {
write!(f, "invalid global heap version: {v}")
}
FormatError::GlobalHeapObjectNotFound {
collection_address,
index,
} => {
write!(
f,
"global heap object not found: collection {collection_address:#x}, index {index}"
)
}
FormatError::VlDataError(msg) => {
write!(f, "variable-length data error: {msg}")
}
FormatError::SerializationError(msg) => {
write!(f, "serialization error: {msg}")
}
FormatError::DatasetMissingData => {
write!(f, "dataset is missing data")
}
FormatError::DatasetMissingShape => {
write!(f, "dataset is missing shape")
}
FormatError::InvalidFilterPipelineVersion(v) => {
write!(f, "invalid filter pipeline version: {v}")
}
FormatError::UnsupportedFilter(id) => match crate::filter_registry::known_filter(*id) {
Some((name, Some(feature))) => write!(
f,
"unsupported filter: {id} ({name}; this build lacks the `{feature}` feature)"
),
Some((name, None)) => write!(
f,
"unsupported filter: {id} ({name}, not implemented by clawhdf5)"
),
None => write!(f, "unsupported filter: {id}"),
},
FormatError::FilterError(msg) => {
write!(f, "filter error: {msg}")
}
FormatError::DecompressionError(msg) => {
write!(f, "decompression error: {msg}")
}
FormatError::CompressionError(msg) => {
write!(f, "compression error: {msg}")
}
FormatError::Fletcher32Mismatch { expected, computed } => {
write!(
f,
"fletcher32 mismatch: expected {expected:#010x}, computed {computed:#010x}"
)
}
FormatError::ChunkedReadError(msg) => {
write!(f, "chunked read error: {msg}")
}
FormatError::ChunkAssemblyError(msg) => {
write!(f, "chunk assembly error: {msg}")
}
FormatError::ChecksumMismatch { expected, computed } => {
write!(
f,
"checksum mismatch: expected {expected:#010x}, computed {computed:#010x}"
)
}
FormatError::NestingDepthExceeded => {
write!(f, "maximum nesting/continuation depth exceeded")
}
FormatError::DuplicateDatasetName(name) => {
write!(f, "duplicate dataset name during parallel merge: {name}")
}
FormatError::Overflow(msg) => {
write!(f, "integer overflow: {msg}")
}
FormatError::InvalidObjectHeader(why) => {
write!(f, "corrupt object header: {why}")
}
FormatError::InvalidDatatype(why) => {
write!(f, "invalid datatype: {why}")
}
FormatError::InvalidChunkDimensions(why) => {
write!(f, "invalid chunk dimensions: {why}")
}
FormatError::TruncatedFile {
stored_eof,
actual_len,
} => {
write!(
f,
"truncated file: the superblock records end of file {stored_eof}, \
but the file is {actual_len} bytes"
)
}
FormatError::InvalidLinkName => {
write!(f, "invalid link name: a group entry has an empty name")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FormatError {}