BUG: Fix null metadata cast and unchecked geometry lengths in HDF5ImageIO#6626
Merged
hjmjohnson merged 2 commits intoJul 15, 2026
Merged
Conversation
Contributor
|
| Filename | Overview |
|---|---|
| Modules/IO/HDF5/src/itkHDF5ImageIO.cxx | Adds geometry dataset validation, preserves ITK exception details, and fixes C-string metadata cast handling. |
| Modules/IO/HDF5/test/itkHDF5ImageIOGTest.cxx | Adds tests for geometry validation and C-string metadata round-tripping. |
| Modules/IO/HDF5/test/CMakeLists.txt | Adds the HDF5 GoogleTest driver and output-directory compile definition. |
| Modules/IO/HDF5/itk-module.cmake | Adds test dependencies needed by the new HDF5 GoogleTest target. |
Reviews (2): Last reviewed commit: "BUG: Validate HDF5 geometry dataset leng..." | Re-trigger Greptile
ca2b803 to
c4d44b5
Compare
WriteImageInformation() dereferenced the const-char* dynamic_cast result before checking it, crashing whenever only the non-const overload matched. Each cast result is now checked before use. Addresses item B73 of InsightSoftwareConsortium#6575.
ReadImageInformation() indexed the Origin, Spacing and Dimension vectors up to numDims without checking the file actually stored that many entries, reading past the end of the heap vector before any pixel is touched. The Directions dataset that numDims itself is derived from was likewise unchecked, so a non-square matrix produced direction rows shorter than numDims. Every geometry dataset is now validated before use. The exception descriptions only survive because ExceptionObject is rethrown unchanged rather than being replaced by the "Unspecified error" message of the catch-all handler. Addresses item B74 of InsightSoftwareConsortium#6575.
c4d44b5 to
d4120bb
Compare
d4120bb to
04e4a95
Compare
|
Errors:
|
Member
|
Two force-pushes, one concern each:
What changed in push 2
|
hjmjohnson
approved these changes
Jul 14, 2026
thewtex
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
HDF5ImageIO::WriteImageInformation()dereferences a nulldynamic_castresult for aMetaDataObject<char *>, andReadImageInformation()indexes the geometry vectors tonumDimswithout checking the datasets are actually that long. Addresses B73 and B74 of #6575.Root cause
B73: the C-string branch guards with
cstringObj != nullptr || constCstringObj != nullptrand then unconditionally dereferencesconstCstringObj.MetaDataObject<char *>andMetaDataObject<const char *>are unrelated instantiations, so only one cast can succeed — achar *entry makes the other null, and it is the one that gets dereferenced.B74:
numDimscomes from the Directions dataset, but Origin, Spacing and Dimension are read withReadVectorand then indexed tonumDimsregardless of their own length. A file whose geometry datasets disagree over-reads a heap vector before any pixel is touched.Directionsitself — the datasetnumDimsis derived from — was equally unchecked:ReadDirections()returnsdim[1]rows ofdim[0]entries and never compares the two, so a non-square matrix yields direction rows shorter thannumDims. All four geometry datasets are now validated for an exact length match.Exception descriptions only reach the caller because
ExceptionObjectis now rethrown unchanged: every throw insideReadImageInformation()was previously caught by the trailingcatch (...)and replaced with"Unspecified error occurred during ReadImageInformation".Local validation
New GoogleTest
itkHDF5ImageIOGTest.cxx, fixtures written in the test body, no ExternalData. Assertions match on the exception description, so they cannot pass on an unrelated failure.ReadImageInformationAcceptsConsistentGeometryReadImageInformationRejectsTruncatedOrigin/...Spacing/...Dimension"Unspecified error"descriptionReadImageInformationRejectsNonSquareDirectionsWriteImageInformationCStringMetaDataRoundTrips-O0; also covers theconst char *branchctest -R HDF5: 8/8 pass, no result changes.Note: the
char *metadata test is a-O0/sanitizer canary. At-O1+ the optimizer sinks the dead load of the null cast into the not-taken branch, so the unfixed code does not fault in an optimized build.B75 investigated and withdrawn
The ledger also listed B75 (
Read()passes the file datatype asH5Dread's memory type, so aSTD_I16BEdataset should read back byte-swapped). The code is as described, but the path is unreachable:ReadImageInformation()runsPredTypeToComponentType(), which requires an exactH5Tequalmatch against aNATIVE_*predtype and throws otherwise — and it populatesm_VoxelDataSet, soRead()cannot run before it succeeds. Writing aSTD_I16BEVoxelData and reading it back throws duringReadImageInformation(); withSTD_I16LEon this host the file type is bit-identical toNATIVE_SHORTand the line is a no-op. There is no reachable failure to fix, so no change was made.Known limitations left for follow-up
WriteString(const std::string &, const char * s)doesstd::string _s(s)with no null check, so aMetaDataObject<char *>holdingnullptris still UB. Out of scope for B73, which concerns the null cast rather than the null value.WriteImageInformation()silently drops metadata whose type is outside the hardcoded cast list (e.g.Array<long long>, which the reader can itself produce).ResetToInitialState(), so a rejected file leaves the ImageIO holding an open HDF5 handle.AI assistance