]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
IMPORT: slz/uslz: don't reset the drain offset when nothing was decoded
authorAurelien DARRAGON <adarragon@haproxy.com>
Tue, 11 Aug 2026 16:19:09 +0000 (18:19 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Wed, 12 Aug 2026 07:14:07 +0000 (09:14 +0200)
The drain code in uslz_decode() resets dec_bofs to zero when the amount
of data it can report is zero, in order to handle the case where the
previous drain stopped exactly on the end of the ring and the pending
block restarts at its beginning.

But this test also matches when there is simply nothing pending at all,
which happens on every USLZ_DECODE_OUT_OF_DATA return that could not
decode a single byte, i.e. very often when the caller feeds small input
chunks. In that case dec_bofs was reset while the pending data was in
fact located further in the ring, and the next drain handed the caller a
pointer to the wrong place, silently returning stale bytes.

The decoded stream was correct (the checksum matched), only the pointer
reported to the caller was wrong, which made this hard to notice: zdec
uses 8kB input chunks and almost always has something to drain.

Fix this by only resetting dec_bofs when there really is pending data.

With the new tests/uslztest.sh matrix this moves the number of passing
combinations from 642/1260 to 848/1260, the remaining failures being
pre-existing issues in the raw deflate and zlib trailer handling and in
the gzip header parsing with very small input chunks.

This is libslz upstream commit 747a544fb374e57d443bc553f092120b07c00a60

src/uslz.c

index 67944413bcb5dc300d123978112bc7f272c7652d..f0f513abad6251fcaeb03c0a74961209636de6b2 100644 (file)
@@ -1012,8 +1012,14 @@ enum uslz_decode_ret uslz_decode(struct uslz_stream *state,
                else
                        *decoded_size = state->dec_bsize;
 
-               if (*decoded_size == 0) {
-                       /* wrapping */
+               if (*decoded_size == 0 && state->dec_bsize) {
+                       /* the pending block starts at the beginning of the
+                        * ring because the previous drain stopped on its end.
+                        * Note the test on dec_bsize: reaching this point with
+                        * nothing pending (e.g. out of data before a single
+                        * byte could be decoded) must not move dec_bofs, or
+                        * the next drain would report the wrong location.
+                        */
                        state->dec_bofs = 0;
                        *decoded_size = state->dec_bsize;
                }