Repository navigation
Conversation
When isal_inflate_stateless() returns, it gives back the whole bytes left in its bit buffer, moving next_in back and avail_in up by read_in_length / 8. When the input ends inside a dynamic block header, setup_dynamic_header() can read up to 57 bits past the end of the input before it checks for the end and returns ISAL_END_INPUT, which leaves read_in_length negative. The give-back then moves next_in forward, up to 7 bytes past the end of the input, and avail_in, which is 0 by then, wraps to about 4 GB. Give back bytes only when read_in_length is positive. At 0 there is nothing to give back, and when it is negative, all of the input has been read and avail_in is already 0. isal_inflate() restores its bit buffer itself when the input ends inside a header, so it does not have this problem. Add a test to igzip_rand_test that inflates every prefix of a stream that starts with a dynamic block, and checks that next_in + avail_in stays at the end of the input. Signed-off-by: Parker Loflin <parker@cronstable.dev>
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When the input of
isal_inflate_stateless()ends inside a dynamic block header, it can return withnext_inup to 7 bytes past the end of the input andavail_inwrapped to about 4 GB. This PR fixes that.The bug
Before returning,
isal_inflate_stateless()gives the unread whole bytes in its bit buffer back to the input:next_in -= read_in_length / 8andavail_in += read_in_length / 8.setup_dynamic_header()checks that 14 bits of input are left, reads them, and then reads the code length code lengths (up to 57 more bits) before checking whether the input has ended. It then returnsISAL_END_INPUTwithread_in_lengthas low as -57, so the give-back movesnext_inforward instead of back, up to 7 bytes past the end of the input, and wrapsavail_in, which is 0 at that point. TheISAL_INVALID_BLOCKreturns of a truncated header can do the same.The code is shared C, so every decoder has the bug. Stateful
isal_inflate()restores the bit buffer itself when the input ends inside a header, so it is not affected.Changes
Give back bytes only when
read_in_lengthis positive: at 0 there is nothing to give back, and below 0 all of the input has been read andavail_inis already 0. The commit also adds a test toigzip_rand_testthat inflates every prefix of a stream that starts with a dynamic block, stateless, and checks thatnext_in + avail_instays at the end of the input.Conflicts
This PR conflicts with #439 in
igzip/igzip_rand_test.c.Testing
On Linux x86-64:
make -f Makefile.unx checkpasses for the x86-64 build andarch=noarch, andtools/check_format.shpasses. Without the fix, the new test fails with both builds: for example, inflating the first 3 bytes of the stream returnsISAL_END_INPUTwithnext_in6 bytes past the end of the input andavail_in4294967290.arch=noarch), stateless and stateful with random buffer sizes,next_in + avail_inmisses the end of the input in 41 cases on master, all stateless inflates whose input ends inside a dynamic block header, and in none with this PR.ISAL_END_INPUTreturns, where master'savail_inwraps and this PR's is 0.On Windows 11 (Ryzen 7 9800X3D), the mingw-w64 x86-64 build passes
make checknatively, including the new test.On aarch64:
make checkpasses on macOS (Apple M4) withhost_cpu=aarch64 arch=aarch64and witharch=noarch, and on Linux in Docker (GCC 14.2) withMakefile.unxand autotools. Without the fix, the new test fails with both macOS builds, with the same numbers as on x86.PROT_NONEpage. On master,next_in + avail_inmisses the end of the input in 929 of them, all stateless inflates of truncated input (928ISAL_END_INPUTreturns and 1ISAL_INVALID_BLOCK), withnext_in1 to 6 bytes past the end. With this PR it is at the end in all 120,000, and the return codes,total_outand output match master's.