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]>
509 lines
20 KiB
Rust
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 {}
|