Skip to content

BUG: Do fslio's size and offset arithmetic at 64-bit width - #128

Open
hjmjohnson wants to merge 2 commits into
stack/pr-fix-sign-conversionfrom
stack/fix-fslio-64bit-arithmetic
Open

hjmjohnson wants to merge 2 commits into
stack/pr-fix-sign-conversionfrom
stack/fix-fslio-64bit-arithmetic

Conversation

@hjmjohnson

Copy link
Copy Markdown
Member

Re-submission of #73, reverted from master on 2026-09-24. Content is
unchanged from the original.

Position 6 of 11 in the deep stack. Base: stack/pr-fix-sign-conversion.

Based on the pull request above it in the stack, so the diff shown here is
this change alone. Merge the stack bottom-up.

Stack order

# branch base
1 stack/test-regression-coverage master
2 stack/pr-fix-alloc-null-checks stack/test-regression-coverage
3 stack/fix-axml-skip-depth stack/pr-fix-alloc-null-checks
4 stack/pr-fix-analyzer-leaks stack/fix-axml-skip-depth
5 stack/pr-fix-sign-conversion stack/pr-fix-analyzer-leaks
6 stack/fix-fslio-64bit-arithmetic <- this PR stack/pr-fix-sign-conversion
7 stack/fix-cifti-null-stream stack/fix-fslio-64bit-arithmetic
8 stack/pr-fix-calloc-transposed-args stack/fix-cifti-null-stream
9 stack/pr-fix-shorten-64-to-32 stack/pr-fix-calloc-transposed-args
10 stack/fix-image-read-complex-check stack/pr-fix-shorten-64-to-32
11 stack/pr-fix-xml-read-errors stack/fix-image-read-complex-check

The order is the order these changes sat on master before the revert, so
it builds and tests at every step.

Commits introduced by this PR
  • BUG: Do fslio's size and offset arithmetic at 64-bit width
  • ENH: Test that FslGetVolSize forms its product at 64-bit width

Ordering for all the re-submitted work is tracked in #84.

A byte count or file offset is declared size_t, but the expression
assigned to it multiplies ints, so the arithmetic happens at 32 bits
and only the result is widened:

    size_t FslGetVolSize(...)
    { return (nx * ny * nz); }           /* int product */

    offset = ((ydim * zVox + yVox) * xdim + xVox) * wordsize;

For a volume over 2GB the product overflows before it is ever assigned
to the size_t, so the destination type buys nothing.  The signed
overflow is undefined, so whether the wide value survives is up to the
optimizer: clang folds it to the correct 64-bit result at -O2 and
yields 0 at -O0.  Affects FslGetVolSize(), FslWriteVolumes(),
FslReadSliceSeries(), FslReadRowSeries() and FslReadTimeSeries().
Each operand is now widened before the multiply rather than after.

The slbytes and volbytes assignments in FslReadSliceSeries() and
FslReadTimeSeries() are left alone because another open pull request
changes those exact lines.

(cherry picked from commit aee824e)
The test drives FslGetVolSize() with dimensions whose product exceeds
INT_MAX and asserts the exact 64-bit value, plus a small volume so an
over-broad change cannot pass it.  It fails in a -O0 build without the
widening fix and passes with it; at -O2 clang folds the signed overflow
to the wide result, so only the unoptimized build observes it.

(cherry picked from commit 87b5264)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants