Skip to content

BUG: Stop iterating images that have no pixel buffer - #7041

Open
hjmjohnson wants to merge 7 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix-null-buffer-iterators
Open

hjmjohnson wants to merge 7 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix-null-buffer-iterators

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Stop building image iterators over images that have no pixel buffer, which offset a null pointer (undefined behavior that UBSan reports as pointer overflow). Each replacement visits the same indices, or draws the same random indices, in the same order, so results are unchanged. Closes #7039.

Commit Code path
BUG: Compute scanline neighbor offsets … ScanlineFilterCommon::SetupLineOffsets (connected-component and label filters)
BUG: Iterate virtual-domain indices … v4 metric GetValueAndDerivative and joint-histogram threaders; REGULAR sampling in ImageRegistrationMethodv4
ENH: Draw ImageRandomConstIteratorWithIndex positions … detail::RandomIndexInRegion, the iterator's index draw, usable without an image
BUG: Draw RANDOM metric samples … RANDOM sampling in ImageRegistrationMethodv4
BUG: Scan the ANTS correlation window … ANTS neighborhood-correlation threader, dense and sparse
BUG: Sample the scales-estimator virtual domain … RegistrationParameterScalesEstimator region and random sampling
ENH: Test v4 registration paths … GoogleTest running every path above
What changed
  1. Scanline offsets come from ConnectedImageNeighborhoodShape, which generates the face- or fully-connected offsets without an image, in the same ascending order setConnectivity() / setConnectivityPrevious() produce; the "previous" neighbors are the first half of the symmetric set.
  2. Dense threaders and REGULAR sampling read only the index, so they iterate ImageRegionIndexRange over the same region.
  3. RANDOM sampling (registration method and scales estimator) calls detail::RandomIndexInRegion, which ImageRandomConstIteratorWithIndex::RandomJump now also calls, with a MersenneTwisterRandomVariateGenerator created and seeded at the same point as the iterator's, so the drawn indices are the same.
  4. The ANTS threader scanned with a ConstNeighborhoodIterator. A small protected nested iterator provides what the scan reads (center and neighbor indices, whether a neighbor is inside the buffered region, the window size, the region start) from the region and a Neighborhood of the same radius.
  5. Includes of the replaced iterators stay, guarded by !ITK_LEGACY_REMOVE && !ITK_FUTURE_LEGACY_REMOVE, because downstream code relies on them transitively (ANTs' antsAI.cxx uses both region iterators without including them).
Testing
  • UBSan: a reproducer covering every path (scanline filter, MeanSquares, JointHistogramMutualInformation, and ANTS metrics, registration with NONE/REGULAR/RANDOM sampling, scales estimator with central-region and random sampling), compiled with -fsanitize=undefined against one ITK build: 16 reports at 13 sites with main's headers, 0 with this branch's, and identical printed values (metric values, derivatives, registration results).
  • Local: macOS arm64, conda clang, Release; every commit builds with 0 warnings; the 999 tests labeled ITKCommon, ITKImageLabel, ITKLabelMap, ITKConnectedComponents, ITKMetricsv4, ITKRegistrationMethodsv4, and ITKOptimizersv4 pass; ANTs' antsAI.cxx compiles against these headers with ITK_LEGACY_REMOVE; the new GoogleTest also compiles with ITK_LEGACY_REMOVE; pre-commit run --all-files passes.
  • Downstream forest (ITK with every default module and Python wrapping, consumers built against it), with this PR and BUG: Invert scaled and skewed versor transforms without throwing #7056 merged (BRAINSTools needs BUG: Invert scaled and skewed versor transforms without throwing #7056 to pass on main): ANTs builds and passes 179/179, BRAINSTools builds and passes 458/458. An earlier run caught ANTs relying on the iterator includes, which is why they stay.

AI assistance (Claude Code) found the defect with a UBSan build of BRAINSTools, wrote the reproducer and the changes, and ran the tests; the author reviewed every line.

@github-actions github-actions Bot added type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances area:Filtering Issues affecting the Filtering module area:Registration Issues affecting the Registration module labels Oct 30, 2026
@hjmjohnson hjmjohnson changed the title BUG: Stop iterating images that have no pixel buffer WIP: BUG: Stop iterating images that have no pixel buffer Oct 30, 2026

@dzenanz dzenanz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This partly duplicates code. I guess it is unavoidable.

@dzenanz
dzenanz requested a review from seanm October 30, 2026 18:10
@hjmjohnson
hjmjohnson force-pushed the fix-null-buffer-iterators branch 2 times, most recently from 3d537d2 to bb6cb8e Compare October 31, 2026 19:48
@github-actions github-actions Bot added type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct area:Numerics Issues affecting the Numerics module and removed type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances labels Oct 31, 2026
@hjmjohnson hjmjohnson changed the title WIP: BUG: Stop iterating images that have no pixel buffer BUG: Stop iterating images that have no pixel buffer Oct 31, 2026
@hjmjohnson

Copy link
Copy Markdown
Member Author

Two force-pushes: 4227060c8d6...3d537d24fb5 is a plain rebase on main (patches unchanged); 3d537d24fb5...bb6cb8e6142 is the content. The second removes the duplicated connectivity code (ConnectedImageNeighborhoodShape now supplies the scanline offsets), covers two more paths that iterated the unallocated virtual image (the ANTS neighborhood-correlation threader and RegistrationParameterScalesEstimator), splits RANDOM sampling into its own commit, and adds a GoogleTest. ANTs and BRAINSTools build and pass all their tests with it (and #7056).

@hjmjohnson

Copy link
Copy Markdown
Member Author

@dzenanz The duplication is gone: the scanline offsets now come from ConnectedImageNeighborhoodShape, which generates the same connected offsets in the same order without an image. What remains is RANDOM sampling repeating the iterator's few-line random draw, which uses ImageRegion::ComputeOffsetTable and ImageHelper::ComputeIndex for the index mapping.

@hjmjohnson
hjmjohnson marked this pull request as ready for review October 31, 2026 19:53
@greptile-apps

greptile-apps Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The changes reach registration metrics, optimizers, and image-label filtering.

The PR appears safe to merge, with a non-blocking improvement to random-sampling tests.

Findings

  1. P2 Wrong samples can pass ▶

Summary

Replaces pixel-buffer iterators with index-only traversal where images have no allocated pixels.

  • Scanline filters get neighbor offsets directly from the connectivity shape.
  • Registration metrics process virtual grids without reading virtual pixels.
  • Registration sampling selects virtual-domain points without pixel iterators.
  • Scale estimation samples virtual domains without pixel iterators.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  R[Image region without allocated pixels] --> D[Dense traversal]
  R --> S[Random sampling]
  R --> N[Neighborhood traversal]
  D --> I[ImageRegionIndexRange]
  S --> G[Random position]
  G --> C[ImageHelper ComputeIndex]
  N --> O[Region bounds and neighborhood offsets]
  I --> P[Indices and physical points]
  C --> P
  O --> P
Loading

Reviews (1) · Last reviewed commit: "ENH: Test v4 registration paths that vis..." · Reviewed by Greptile

@hjmjohnson
hjmjohnson force-pushed the fix-null-buffer-iterators branch from bb6cb8e to e383983 Compare October 31, 2026 20:09
@github-actions github-actions Bot added the type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances label Oct 31, 2026
@hjmjohnson
hjmjohnson force-pushed the fix-null-buffer-iterators branch from e383983 to 2ea9e4c Compare October 31, 2026 20:11
@hjmjohnson

Copy link
Copy Markdown
Member Author

Two force-pushes: bb6cb8e6142...e3839839b42 is content only (a one-line comment on the ANTS scan iterator, a corrected one-line test comment, commit bodies wrapped at 72 columns); e3839839b42...2ea9e4c877d is a plain rebase on main (patches unchanged).

…ed image

SetupLineOffsets built a ConstShapedNeighborhoodIterator over a
PretendImage that is never allocated, only to read the active neighbor
offsets. Constructing it offsets the null buffer, undefined behavior
that UBSan reports from ConnectedComponentImageFilter and the other
scanline label filters.

Generate the offsets with ConnectedImageNeighborhoodShape, which needs
no image and yields them in the order setConnectivity() and
setConnectivityPrevious() produce; the "previous" neighbors are the
first half of the symmetric set.

Addresses InsightSoftwareConsortium#7039.
…l image

ImageToImageMetricv4 never allocates its virtual image, yet the dense
GetValueAndDerivative threader, the joint-histogram PDF threader, and
REGULAR metric sampling in ImageRegistrationMethodv4 walked it with
ImageRegionConstIteratorWithIndex, which computes pixel pointers from
the null buffer: undefined behavior that UBSan reports as pointer
overflow.

All three read only the index, so iterate ImageRegionIndexRange over the
same region, which visits the same indices in the same order.

Addresses InsightSoftwareConsortium#7039.
…elper

Code that samples a region at random without an allocated image, such
as the v4 registration virtual domain, has to reproduce the iterator's
index draw exactly to keep its samples, and so registration results,
unchanged. Move the draw into detail::RandomIndexInRegion, which takes
only a generator and a region, and call it from RandomJump. The
arithmetic and the generator calls are unchanged.

Addresses InsightSoftwareConsortium#7039.
RANDOM metric sampling in ImageRegistrationMethodv4 walked the virtual
image with ImageRandomConstIteratorWithIndex, whose every jump offsets
the null buffer pointer.

Draw the same indices with detail::RandomIndexInRegion, the iterator's
own draw, from a MersenneTwisterRandomVariateGenerator created and
seeded at the same point as the iterator's. The sampled points, and so
registration results, are unchanged.

Addresses InsightSoftwareConsortium#7039.
The ANTS neighborhood-correlation threader scanned the virtual image
with a ConstNeighborhoodIterator, in both its dense and sparse paths.
The metric never allocates that image, so constructing and advancing
the iterator offsets a null buffer pointer.

The scan reads only indices: the center index, each neighbor's index,
whether a neighbor lies inside the buffered region, the window size,
and the region start. A small nested iterator provides exactly those
from the region and a Neighborhood of the same radius, in the same
order, so the correlation sums and the metric are unchanged.

Addresses InsightSoftwareConsortium#7039.
… image

RegistrationParameterScalesEstimator sampled the metric's virtual image,
which is never allocated, with ImageRegionConstIteratorWithIndex for
central-region sampling and ImageRandomConstIteratorWithIndex for random
sampling. Both offset the null buffer pointer. The estimator runs from
ImageRegistrationMethodv4 whenever a scales estimator is set.

Iterate ImageRegionIndexRange for the region, and draw random indices
with detail::RandomIndexInRegion from a generator created at the same
point as the iterator's. The sample points, and so the estimated
scales, are unchanged.

Addresses InsightSoftwareConsortium#7039.
@hjmjohnson
hjmjohnson force-pushed the fix-null-buffer-iterators branch from 2ea9e4c to e964468 Compare October 31, 2026 20:51
@github-actions github-actions Bot added the area:Core Issues affecting the Core module label Oct 31, 2026
@hjmjohnson

Copy link
Copy Markdown
Member Author

Force-push 2ea9e4c877d → e9644689f25 is content only (same base, ffda1aac7d0). It applies the three remaining review suggestions:

  • the iterator includes kept for downstream code are now guarded by !ITK_LEGACY_REMOVE && !ITK_FUTURE_LEGACY_REMOVE instead of carrying a comment;
  • the ANTS threader's nested VirtualNeighborhoodScanIterator and ScanIteratorType are protected;
  • a new commit moves the random iterator's index draw into detail::RandomIndexInRegion, which RandomJump, RANDOM registration sampling, and the scales estimator now share.
Testing

Every commit builds with 0 warnings; 999 tests (ITKCommon plus the six affected modules) pass; ANTs' antsAI.cxx still compiles against these headers with ITK_LEGACY_REMOVE; pre-commit run --all-files passes.

Run every code path that reads the never-allocated virtual image of an
ImageToImageMetricv4: the MeanSquares, JointHistogramMutualInformation,
and ANTS neighborhood-correlation metrics, ImageRegistrationMethodv4
with NONE, REGULAR, and RANDOM sampling, and the PhysicalShift scales
estimator with central-region and random sampling. Each checks that
the virtual image has no buffer and that the results are finite. Built
with -fsanitize=undefined -fno-sanitize-recover=undefined, the test
fails if one of these paths iterates the null buffer again.

Adds the module's first GoogleTest, so ITKGoogleTest joins its
TEST_DEPENDS.

Addresses InsightSoftwareConsortium#7039.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Core Issues affecting the Core module area:Filtering Issues affecting the Filtering module area:Numerics Issues affecting the Numerics module area:Registration Issues affecting the Registration module type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Iterators over unallocated images offset a null buffer pointer (UBSan, release-5.4)

2 participants