Skip to content

BUG: Stop returning -1 from functions that return size_t - #111

Open
hjmjohnson wants to merge 2 commits into
masterfrom
pr/fix-size-t-error-returns
Open

hjmjohnson wants to merge 2 commits into
masterfrom
pr/fix-size-t-error-returns

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

znzread(), znzwrite(), and nifti_read_buffer() return size_t but reported errors as -1 (SIZE_MAX), which every < caller read as a complete transfer. They now return 0 on error.
Effect for ITK and other readers: a truncated or corrupt .nii/.nii.gz now makes nifti_image_read() return NULL instead of loading an image with an uninitialized tail.

Re-submission of #43, reverted from master on 2026-09-24 so it can be reviewed before merging. Review round 1 added the .nii.gz test cases and the return-contract notes.

Base: master. Independent: nothing has to land before it.

Commits
  • BUG: Stop returning -1 from functions that return size_t
  • ENH: Test that a truncated or corrupt image is rejected
Test coverage

nifti_short_read_test writes a reference image and three damaged copies:

  • a .nii 1000 bytes short: fails without the nifti1_io.c fix;
  • a .nii.gz 1000 bytes short: gzread returns a short count, rejected with or without the fix;
  • a .nii.gz whose deflate stream hits a reserved block type mid-image: gzread returns -1, fails without the znzlib.c fix.

The znzwrite() error branch is not exercised; gzwrite() reports errors as 0, not a negative count.

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

@hjmjohnson
hjmjohnson marked this pull request as ready for review September 24, 2026 12:17
@hjmjohnson
hjmjohnson force-pushed the pr/fix-size-t-error-returns branch from d9f5d2d to 987acbd Compare October 6, 2026 14:35
gdevenyi and others added 2 commits November 6, 2026 10:53
znzread(), znzwrite() and nifti_read_buffer() all return size_t and
all returned -1 to report an error.  In a size_t that value is
SIZE_MAX, larger than any length a caller can have asked for, so a
caller that tests the result with '<' reads the error as a complete
transfer:

    ii = nifti_read_buffer(fp, nim->data, ntot, nim);
    if( ii < ntot ){ ... }          /* SIZE_MAX < ntot is false */

The visible effect is that a truncated image loads as if it were
whole: nifti_image_read() prints its short-read warning, ignores the
failure and returns an image whose tail is uninitialized heap.
FslReadVolumes() has the same problem one level up, dividing the
returned byte count by the volume size to report SIZE_MAX/volbytes
volumes read.

These now return 0 on error, which every one of these callers already
treats as failure.  The return contract is now stated beside
nifti_read_buffer() and in znzlib.h.  The published return types in
znzlib.h and nifti1_io.h are unchanged.

The nifti2 equivalents return int64_t, where -1 is representable,
and are left alone.
Writes a 31x31x31 float image and three damaged copies of it, and
asserts nifti_image_read() returns NULL for each copy and a populated
image for the original, so a blanket refusal cannot pass:

- a .nii 1000 bytes short (the fread path of znzread);
- a .nii.gz 1000 bytes short, where gzread returns a short count;
- a .nii.gz whose deflate stream ends in a block of reserved type
  after half the file, so gzread fails with -1 inside the image data.

Without the fix the first and third copies come back accepted with
nvox=29791 and an uninitialized tail.  The truncated .nii.gz is
rejected with or without the fix; it guards the short-count path.
The znzwrite() error branch is not exercised, since gzwrite() does not
return a negative count.
@hjmjohnson
hjmjohnson force-pushed the pr/fix-size-t-error-returns branch from 987acbd to 19d840a Compare November 6, 2026 16:54
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