Skip to content

BUG: Bound the formatted write in znzprintf, and end the va_list on failure - #110

Open
hjmjohnson wants to merge 4 commits into
masterfrom
pr/fix-vsprintf-bounded
Open

hjmjohnson wants to merge 4 commits into
masterfrom
pr/fix-vsprintf-bounded

Conversation

@hjmjohnson

Copy link
Copy Markdown
Member

Re-submission of #41, reverted from master on 2026-09-24 so it can be
reviewed before merging. Content is unchanged from the original.

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

Commits
  • BUG: Bound the formatted write in znzprintf, and end the va_list
  • BUG: Report a znzprintf failure as a failure
  • ENH: Cover the znzprintf truncation path
  • COMP: Declare the two znzlib functions that no header declares

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

gdevenyi and others added 4 commits September 24, 2026 07:10
znzprintf() built its output with vsprintf(), which has no bound; the
buffer was sized strlen(format) + 1000000 under a comment reading
"overkill I hope".  A single %s argument longer than a megabyte writes
past the end of the allocation.  vsnprintf() with the size already
being computed turns that overflow into a reported truncation.

The same block also returned on calloc failure without calling
va_end(), leaving the va_list unterminated.

This is the only unbounded formatted write in the tree; there is no
sprintf() and no gets() anywhere.

(cherry picked from commit 57a61a7)
The preceding commit bounds the write but still returned the gzprintf
count after a truncation, so a caller could not tell.  It also
returned 0 for an allocation failure, and the function returns 0 for a
NULL stream.  The printf family reports failure with a negative value;
0 is a successful empty write, so it cannot carry either meaning.

All three now return -1, and a truncated result is not written at all:
a partial record reported as a whole one is worse than nothing.

znzprintf sits inside COMPILE_NIFTIUNUSED_CODE in both znzlib.c and
znzlib.h and is absent from libznz.a in a default build, so this is a
latent defect in code no released configuration compiles.

(cherry picked from commit ac7ec09)
znzprintf has no caller in this tree and is compiled only under
COMPILE_NIFTIUNUSED_CODE, so nothing reached it.  The test builds its
own copy of znzlib.c with that definition and drives a 2 MB argument
through the 1000001 byte buffer.

Without the bound the write overflows the allocation, and the call
reports success; the test pins the negative return, and pins the
ordinary write and the resulting file content so an over-broad fix
that refuses everything does not pass.

(cherry picked from commit 0350b30)
znzflush() and znzeof() are defined inside COMPILE_NIFTIUNUSED_CODE and
declared nowhere, unlike znzgets(), znzputc() and znzgetc() beside them.
Nothing compiled that block until a test in this branch did, so the
omission was invisible.

They are declared where the rest of the block is declared, so the
warning set the project already requires stays clean when the block is
built.

(cherry picked from commit 8449a1f)
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