Skip to content

BUG: Fix the REJECT_COMPLEX path in nifti_image_read - #74

Merged
hjmjohnson merged 2 commits into
InsightSoftwareConsortium:masterfrom
gdevenyi:fix/image-read-complex-check
Sep 23, 2026
Merged

hjmjohnson merged 2 commits into
InsightSoftwareConsortium:masterfrom
gdevenyi:fix/image-read-complex-check

Conversation

@gdevenyi

Copy link
Copy Markdown

The block has two defects, and they interlock.

It sits before the if( nim == NULL ) test but reads nim->datatype, so a header that fails to convert is dereferenced as NULL rather than reported. And it calls exit(13), which ends the caller's process from inside a library and leaks the open file and hfile on the way out.

Moving it after the NULL check is what makes returning possible, so both are fixed together: the block now runs on a nim that exists, frees the image, closes the file, releases hfile and returns NULL.

The message also loses its "64", which was wrong for the 128 and 256 cases.

Split out of #54, which keeps the nifti_findhdrname() half. The two touch different functions and do not conflict.

REJECT_COMPLEX is off by default but is a supported configuration: -DFSLSTYLE:BOOL=ON turns it on, and nifti2/Makefile defines it always. Built and tested with -DFSLSTYLE=ON.

🤖 Generated with Claude Code

@hjmjohnson
hjmjohnson force-pushed the fix/image-read-complex-check branch 2 times, most recently from 0518a5e to 80aad59 Compare September 22, 2026 02:10
hjmjohnson pushed a commit that referenced this pull request Sep 22, 2026
Under FSLSTYLE the block that reports "Multiple possible filenames" has
three defects, and they cannot be separated.

  free(basename);                                  <- freed here
  char *gzname = calloc(...);                      <- unchecked
  strcpy(gzname, hdrname);                         <- used at once
  ...
    fprintf(stderr,"... %s\n", basename);          <- read after free
    exit(134);                                     <- kills the caller

basename is read by the message, so it cannot be freed at the top.
gzname goes straight into strcpy(), so it has to be checked.  And once
the function returns NULL instead of calling exit(), every path out has
to release what it holds, which is what ties the three together: moving
the free of basename decides what the error paths must free, and there
are no error paths to speak of until exit() is gone.

FSLSTYLE is off by default but is a supported configuration:
-DFSLSTYLE:BOOL=ON turns it on, and nifti2/Makefile defines it always.
Built and tested with -DFSLSTYLE=ON.

The nifti_image_read() half of the original change is now #74.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hjmjohnson pushed a commit that referenced this pull request Sep 22, 2026
Under FSLSTYLE the block that reports "Multiple possible filenames" has
three defects, and they cannot be separated.

  free(basename);                                  <- freed here
  char *gzname = calloc(...);                      <- unchecked
  strcpy(gzname, hdrname);                         <- used at once
  ...
    fprintf(stderr,"... %s\n", basename);          <- read after free
    exit(134);                                     <- kills the caller

basename is read by the message, so it cannot be freed at the top.
gzname goes straight into strcpy(), so it has to be checked.  And once
the function returns NULL instead of calling exit(), every path out has
to release what it holds, which is what ties the three together: moving
the free of basename decides what the error paths must free, and there
are no error paths to speak of until exit() is gone.

FSLSTYLE is off by default but is a supported configuration:
-DFSLSTYLE:BOOL=ON turns it on, and nifti2/Makefile defines it always.
Built and tested with -DFSLSTYLE=ON.

The nifti_image_read() half of the original change is now #74.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hjmjohnson
hjmjohnson force-pushed the fix/image-read-complex-check branch from 80aad59 to e93a6e0 Compare September 22, 2026 10:49
@hjmjohnson
hjmjohnson force-pushed the fix/image-read-complex-check branch from e93a6e0 to 7d30810 Compare September 22, 2026 13:43
@hjmjohnson

Copy link
Copy Markdown
Member

Half of this PR is now on master and has been dropped; the other half is still needed and now has a regression test. Rebased onto b4876bf.

master commit "BUG: Check the header converted to nifti_image before reading its datatype" already moved the REJECT_COMPLEX block below the if( nim == NULL ) test, in both niftilib/nifti1_io.c and nifti2/nifti2_io.c. That was the NULL-dereference half of this PR, so it is gone from the branch.

What master did not fix is the other half: the block still calls exit(13), and still prints "(COMPLEX64)" for the 128 and 256 cases. Ending the caller's process from inside a library is the thing this project has a standing rule against — nifti_clib is vendored into ITK, AFNI, FSL, dcm2niix and Slicer, and none of them can survive their image reader calling exit(). So this PR is partially superseded, not redundant, and I recommend keeping it open.

What the branch is now

Two commits on b4876bf:

  1. BUG: Return from the REJECT_COMPLEX path instead of calling exit() — frees the image, closes the file, releases hfile, returns NULL, and corrects the message text. Both libraries.
  2. ENH: Test that a complex image is rejected without ending the process.

The ancestor commits the branch carried are already upstream under new SHAs and dropped out during the rebase.

Message cleanup

Removed the Co-Authored-By: naming an AI tool and the "Generated with" line with its URL. Replaced the bare #54 cross-reference with words (this is a fork, so the number names a different PR upstream). Rewrote the body so it describes only what is left to do, since the NULL-check half is no longer part of the change.

Test and red proof

niftilib/nifti_reject_complex_test.c, registered as ctest nifti_reject_complex and only when FSLSTYLE_REJECT_COMPLEX is on. It writes a DT_COMPLEX64 image and a DT_FLOAT32 image, then asserts nifti_image_read() returns NULL for the first and a usable image for the second — the second assertion is there so a blanket refusal cannot pass.

With the fix reverted to exit(13):

1: Image Exception Unsupported datatype (COMPLEX64): use fslcomplex to manipulate: reject_complex_cplx.nii
1/1 Test #1: nifti_reject_complex .............***Failed    0.72 sec

The test process is killed at status 13 before it reaches either assertion, which is the defect stated directly. With the fix:

1: Image Exception Unsupported datatype (COMPLEX): use fslcomplex to manipulate: reject_complex_cplx.nii
1: REJECT_COMPLEX test passed.
1/1 Test #1: nifti_reject_complex .............   Passed    0.60 sec
Build and test

-DUSE_FSL_CODE=ON -DUSE_NIFTI2_CODE=ON -DUSE_CIFTI_CODE=ON -DNIFTI_BUILD_APPLICATIONS=ON, with and without -DFSLSTYLE=ON. 365/365 pass with FSLSTYLE=ON, 363/363 without.

Four of the added lines are re-indentation: the block's closing } and #endif sat at two spaces against the function's three-space body, and are now aligned. They are inside the block being rewritten; no other formatting changed.

gdevenyi and others added 2 commits September 22, 2026 19:40
nifti_image_read() ended the calling process with exit(13) when it met
a complex datatype, leaking the open file and hfile on the way out.  A
library reports the condition to its caller: the block now frees the
image, closes the file, releases hfile and returns NULL, which is how
every other rejection in the function behaves.

The message also loses its "64", which was wrong for the 128 and 256
cases.

REJECT_COMPLEX is off by default but is supported: -DFSLSTYLE:BOOL=ON
turns it on, and nifti2/Makefile defines it always.
Writes a DT_COMPLEX64 image and a DT_FLOAT32 image, then asserts
nifti_image_read() returns NULL for the first and a usable image for
the second, so a blanket refusal cannot pass.  Registered only when
FSLSTYLE_REJECT_COMPLEX is on.  Against exit(13) the test process dies
with status 13 before reaching either assertion.
@hjmjohnson
hjmjohnson force-pushed the fix/image-read-complex-check branch from 7d30810 to 5c219c0 Compare September 23, 2026 00:41
@hjmjohnson
hjmjohnson merged commit 461cd33 into InsightSoftwareConsortium:master Sep 23, 2026
26 checks passed
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.

2 participants