Skip to content

BUG: Check dim[0] before using it to index dim[] in the NIFTI-2 converter - #64

Merged
hjmjohnson merged 1 commit into
InsightSoftwareConsortium:masterfrom
gdevenyi:fix/n2-dim0-range
Sep 22, 2026
Merged

hjmjohnson merged 1 commit into
InsightSoftwareConsortium:masterfrom
gdevenyi:fix/n2-dim0-range

Conversation

@gdevenyi

Copy link
Copy Markdown

nifti_convert_n2hdr2nim() uses nhdr.dim[0], the number of dimensions,
as a loop bound over the eight-element dim[] array:

for( ii=2 ; ii <= nhdr.dim[0] ; ii++ )        ...
for( ii=nhdr.dim[0]+1 ; ii <= 7 ; ii++ )      ...
for( ii=1 ; ii <= nhdr.dim[0] ; ii++ )        ...

It never checks that dim[0] is in [0,7]. The NIFTI-1 converter gets
that check for free, because need_nhdr_swap() rejects a dim[0] outside
[1,7] in either byte order, but the NIFTI-2 path decides swapping from
sizeof_hdr and reaches the loops with whatever the file said.

A NIFTI-2 header with a large negative dim[0] therefore starts the
second loop at a wild negative index, reads far outside the header and
segfaults. This is not confined to the header API: nifti_image_read()
gets there for any file with a valid 540 byte NIFTI-2 header, so a
604 byte file crashes nifti_tool:

$ nifti_tool -disp_nim -infiles bad_n2_dim0.nii
nifti2_io.c:5079: runtime error: index -6727636073941130588 out of
    bounds for type 'int64_t[8]'
AddressSanitizer: SEGV ... in nifti_convert_n2hdr2nim

dim[0] is now range checked in the same place, and in the same style,
as the dim[1] check just below it. Zero stays acceptable, as it is on
the NIFTI-1 side. Valid headers are unaffected: dim[0] outside [0,7]
has no meaning in either format.

Found by fuzzing nifti_convert_n2hdr2nim() with clang's libFuzzer under
AddressSanitizer.


How to reproduce, and the sanitizer builds used, are in the commit message. Found with clang libFuzzer harnesses over nifti_image_read(), the two header converters, nifti_image_from_ascii() and axml_read_buf(), run under AddressSanitizer, UndefinedBehaviorSanitizer and MemorySanitizer.

One commit on master, independent of my other open PRs. ctest passes on master with this branch alone (343/343, excluding the nifti_c22_copy_image failure that #31 fixes), and on the union of all my branches under ASan+UBSan, TSan and MSan.

@gdevenyi
gdevenyi marked this pull request as ready for review August 15, 2026 21:30
…rter

nifti_convert_n2hdr2nim() uses nhdr.dim[0], the number of dimensions,
as a loop bound over the eight-element dim[] array:

    for( ii=2 ; ii <= nhdr.dim[0] ; ii++ )        ...
    for( ii=nhdr.dim[0]+1 ; ii <= 7 ; ii++ )      ...
    for( ii=1 ; ii <= nhdr.dim[0] ; ii++ )        ...

It never checks that dim[0] is in [0,7].  The NIFTI-1 converter gets
that check for free, because need_nhdr_swap() rejects a dim[0] outside
[1,7] in either byte order, but the NIFTI-2 path decides swapping from
sizeof_hdr and reaches the loops with whatever the file said.

A NIFTI-2 header with a large negative dim[0] therefore starts the
second loop at a wild negative index, reads far outside the header and
segfaults.  This is not confined to the header API: nifti_image_read()
gets there for any file with a valid 540 byte NIFTI-2 header, so a
604 byte file crashes nifti_tool:

    $ nifti_tool -disp_nim -infiles bad_n2_dim0.nii
    nifti2_io.c:5079: runtime error: index -6727636073941130588 out of
        bounds for type 'int64_t[8]'
    AddressSanitizer: SEGV ... in nifti_convert_n2hdr2nim

dim[0] is now range checked in the same place, and in the same style,
as the dim[1] check just below it.  Zero stays acceptable, as it is on
the NIFTI-1 side.  Valid headers are unaffected: dim[0] outside [0,7]
has no meaning in either format.

Found by fuzzing nifti_convert_n2hdr2nim() with clang's libFuzzer under
AddressSanitizer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014m231RPDZPDbYDVxawxpjG
@hjmjohnson
hjmjohnson merged commit ebff56c into InsightSoftwareConsortium:master Sep 22, 2026
21 checks passed
@hjmjohnson

Copy link
Copy Markdown
Member

The commit messages in this range were rewritten to remove trailers that do not belong in permanent history: Co-Authored-By: naming an AI tool, and Claude-Session: URLs that resolve for nobody. Only messages changed — the tree at the tip of master is byte-identical, and author, committer, and dates are preserved.

This PR's commit on the rewritten master:

  • 5e32002bd7 BUG: Check dim[0] before using it to index dim[] in the NIFTI-2 converter

The SHA recorded above by GitHub is from the pre-rewrite history and no longer resolves.

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