Skip to content

BUG: Widen TileImageFilter auto-size divisor to avoid overflow#6640

Open
hjmjohnson wants to merge 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix-6630-tilefilter-sigfpe
Open

BUG: Widen TileImageFilter auto-size divisor to avoid overflow#6640
hjmjohnson wants to merge 1 commit into
InsightSoftwareConsortium:mainfrom
hjmjohnson:fix-6630-tilefilter-sigfpe

Conversation

@hjmjohnson

Copy link
Copy Markdown
Member

Follow-up to merged #6630 (BUG: Validate TileImageFilter Layout has no zero before the last axis), fixing a division-by-zero this reviewer found in that PR: the divisor used to auto-size the last axis was accumulated in a 32-bit int, which can wrap to exactly 0 for a large Layout.

Root cause
int used = 1;
for (unsigned int d = 0; d < OutputImageDimension - 1; ++d)
{
  used *= m_Layout[d];   // m_Layout[d] is unsigned int
}
outputSize[OutputImageDimension - 1] =
  (static_cast<SizeValueType>(this->GetNumberOfIndexedInputs()) - 1) / used + 1;

m_Layout[d] values are unsigned int. Layout{65536, 65536, 0} gives used = 65536 * 65536 = 2^32, which wraps to exactly 0 in 32-bit arithmetic — turning the division above into a division-by-zero. On architectures whose idiv traps (the dominant CI arch), that's an immediate SIGFPE; on others it falls through to a pathological allocation attempt.

#6630's own new guard (rejecting a zero Layout entry on a non-last axis) doesn't cover this: 65536 and 65536 are both nonzero: the guard the PR added is orthogonal to this defect.

Fix and validation

Widened the accumulator to SizeValueType (64-bit on the platforms that matter) — 65536 * 65536 then computes correctly as 4294967296, well within range — and added an explicit rejection if the widened product is still exactly zero (a defensive backstop, not expected to be reachable in practice).

I attempted an end-to-end regression test reproducing the original overflow-triggering Layout and confirmed it's impractical: the product that overflows is the number of elements TileImageFilter allocates for its internal tile-bookkeeping image (GenerateOutputInformation() calls m_TileImage->Allocate() directly, not deferred to GenerateData()), so any Layout large enough to exercise the 32-bit overflow requires allocating on the order of 2^32 bookkeeping elements — hundreds of GB, regardless of how the axes are split. No practical test can drive the actual overflow through the filter's public API.

pre-commit run --all-files: exit 0. ctest -L ITKImageGrid: 69/69 pass, 0 regressions (including the pre-existing RejectsZeroInNonLastLayoutAxis and RejectsDefaultZeroLayout GTests, unaffected by this change).

The product of the non-last-axis Layout counts was accumulated in a
32-bit int before being used as a division-by-zero-prone divisor to
auto-size the last axis. For a large Layout (e.g. {65536, 65536, 0}),
the product wraps in 32-bit arithmetic and can land on exactly 0,
turning the division into a SIGFPE on architectures whose idiv traps.

Widen the accumulator to SizeValueType and reject the pathological
case where it's still zero.

Forward-fix for the review finding on PR InsightSoftwareConsortium#6630 (merged).
@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 labels Jul 15, 2026
@hjmjohnson hjmjohnson marked this pull request as ready for review July 15, 2026 12:15
@hjmjohnson hjmjohnson requested a review from dzenanz July 15, 2026 12:15
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR widens the accumulator used to auto-size TileImageFilter layouts. The main changes are:

  • Uses SizeValueType for the fixed-axis product.
  • Rejects a product that evaluates to zero.
  • Documents the division-by-zero failure being prevented.

Confidence Score: 4/5

The 32-bit auto-sizing path can still compute an incorrect layout and should be fixed before merging.

SizeValueType remains 32-bit on some configurations. Unsigned multiplication can wrap to a nonzero value that bypasses the new guard. The wrapped divisor can undersize tile bookkeeping and output.

Modules/Filtering/ImageGrid/include/itkTileImageFilter.hxx

T-Rex T-Rex Logs

What T-Rex did

  • Ran a C++17 harness to exercise the nonzero 32-bit overflow and auto-size calculation, confirming the guard bypass for 65537 × 65537.
  • Observed that for 1,000,000,000 indexed inputs, wrapped arithmetic yielded a last-axis size of 7630 instead of 1, showing the auto-size calculation can produce an incorrect result.
  • Compared 32-bit and 64-bit paths and confirmed that the 64-bit SizeValueType produces 4294967296, with auto-size 1 and a clean exit code 0.
  • Collected and reviewed toolchain/build-tree verification logs that document the before/after behavior and the exact commands used for validation.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
Modules/Filtering/ImageGrid/include/itkTileImageFilter.hxx Widens the auto-size divisor and adds a zero check, but nonzero unsigned overflow remains possible on 32-bit configurations.

Reviews (1): Last reviewed commit: "BUG: Widen TileImageFilter auto-size div..." | Re-trigger Greptile

SizeValueType used = 1;
for (unsigned int d = 0; d < OutputImageDimension - 1; ++d)
{
used *= m_Layout[d];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Nonzero Product Overflow Bypasses Guard

On configurations where SizeValueType is 32-bit, a nonzero fixed-axis product larger than 2^32 can wrap to a nonzero value. The used == 0 check then passes, and auto-sizing allocates bookkeeping for fewer tiles than the layout requires, so callers can receive incomplete or undersized output. Check for overflow before each multiplication instead of checking only the final value.

Context Used: AGENTS.md (source)

Artifacts

Repro: C++17 harness exercising the nonzero 32-bit overflow and auto-size calculation

  • Contains supporting evidence from the run (text/x-c++; charset=utf-8).

Repro: compiler and runtime output showing the guard bypass and erroneous derived last-axis size

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

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

Labels

area:Filtering Issues affecting the Filtering module type:Bug Inconsistencies or issues which will cause an incorrect result under some or all circumstances

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants