BUG: Widen TileImageFilter auto-size divisor to avoid overflow#6640
BUG: Widen TileImageFilter auto-size divisor to avoid overflow#6640hjmjohnson wants to merge 1 commit into
Conversation
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).
|
| 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]; |
There was a problem hiding this comment.
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.
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-bitint, which can wrap to exactly 0 for a largeLayout.Root cause
m_Layout[d]values areunsigned int.Layout{65536, 65536, 0}givesused = 65536 * 65536 = 2^32, which wraps to exactly0in 32-bit arithmetic — turning the division above into a division-by-zero. On architectures whoseidivtraps (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
Layoutentry on a non-last axis) doesn't cover this:65536and65536are 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 * 65536then computes correctly as4294967296, 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
Layoutand confirmed it's impractical: the product that overflows is the number of elementsTileImageFilterallocates for its internal tile-bookkeeping image (GenerateOutputInformation()callsm_TileImage->Allocate()directly, not deferred toGenerateData()), so anyLayoutlarge enough to exercise the 32-bit overflow requires allocating on the order of2^32bookkeeping 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-existingRejectsZeroInNonLastLayoutAxisandRejectsDefaultZeroLayoutGTests, unaffected by this change).