From d30faae045124b741c43d0442ee2821fea418732 Mon Sep 17 00:00:00 2001 From: "Gabriel A. Devenyi" Date: Fri, 14 Aug 2026 23:13:02 -0400 Subject: [PATCH 1/2] BUG: Report XML read errors instead of treating them as end of input axml_read_file()'s read loop: blen = fread(buf, 1, bsize, fp); ... done = blen < (unsigned)bsize; A short read is the loop's only stopping condition, so an I/O failure is indistinguishable from reaching the end of the file: the parse stops early and the caller is handed whatever was parsed so far, with no indication that the rest of the document was never read. ferror() is now checked and the failure reported. The loop also called fread() once more after a file ending exactly on a buffer boundary; testing feof() as part of the stopping condition removes that read, which the clang static analyzer flags as a read at EOF. --- cifti/afni_xml.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cifti/afni_xml.c b/cifti/afni_xml.c index 5a92738a..8e55a485 100644 --- a/cifti/afni_xml.c +++ b/cifti/afni_xml.c @@ -197,6 +197,10 @@ afni_xml_list axml_read_file(const char * fname, int read_data) if( reset_xml_buf(xd, &buf, &bsize) ) break; blen = (unsigned)fread(buf, 1, (size_t)bsize, fp); + if( ferror(fp) ) { + fprintf(stderr,"** failed to read XML file '%s'\n", fname); + break; + } /* check for early termination */ bshort = loc_strnlen(buf, blen); @@ -207,7 +211,9 @@ afni_xml_list axml_read_file(const char * fname, int read_data) blen = (unsigned)bshort; } - done = blen < (unsigned) bsize; + /* feof() also stops a file ending exactly on a buffer boundary, + sparing a final read that can only return zero */ + done = blen < (unsigned) bsize || feof(fp); if(xd->verb > 4) fprintf(stderr,"-- XML_Parse # %d\n", pcount); pcount++; From 99392649d9f7669faebd45e84567c06a46d4eac2 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Tue, 22 Sep 2026 08:37:49 -0500 Subject: [PATCH 2/2] ENH: Cover the XML read-error and read-success paths A directory opens but cannot be read, so afni_xml_tool on cifti/testdata reaches the ferror() branch; the test pins the diagnostic rather than the exit status, which was already non-zero before the fix. A second test parses a real document and forbids that diagnostic, so a change that reported a read error unconditionally would not pass. --- cifti/CMakeLists.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cifti/CMakeLists.txt b/cifti/CMakeLists.txt index ef43a4e4..405d87d8 100644 --- a/cifti/CMakeLists.txt +++ b/cifti/CMakeLists.txt @@ -51,6 +51,26 @@ if(NIFTI_BUILD_TESTING AND NIFTI_BUILD_APPLICATIONS) PROPERTIES PASS_REGULAR_EXPRESSION "demo" FAIL_REGULAR_EXPRESSION "no CIFTI extension" ) + # A read failure must be reported, not taken for the end of the document. + # The input is a directory, which opens on POSIX and fails at the first + # read; Windows refuses the open instead, so there is no read to fail. + if( NOT WIN32 ) + add_test( NAME ${TEST_PREFIX}_xml_read_error + COMMAND $ + -input ${CMAKE_CURRENT_LIST_DIR}/testdata ) + set_tests_properties( ${TEST_PREFIX}_xml_read_error + PROPERTIES PASS_REGULAR_EXPRESSION "failed to read XML file" ) + endif() + + # A readable document must still parse, and must not report a read error. + add_test( NAME ${TEST_PREFIX}_xml_read_ok + COMMAND $ + -input ${CMAKE_CURRENT_LIST_DIR}/testdata/mim_known_child.xml + -verb 2 ) + set_tests_properties( ${TEST_PREFIX}_xml_read_ok + PROPERTIES PASS_REGULAR_EXPRESSION "xlist read" + FAIL_REGULAR_EXPRESSION "failed to read XML file" ) + # An extension payload that fills esize-8 with no NUL must not be read # past its end; the regex pins that the payload is still parsed. add_test( NAME ${TEST_PREFIX}_tool_unterminated_cext