From: Willy Tarreau Date: Sun, 23 Aug 2026 10:42:34 +0000 (+0200) Subject: IMPORT: uslz: do not replay the header-detection bytes on the raw deflate path X-Git-Tag: v3.5-dev6~34 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=e0fe1be1c8edb381f99aa221d839cf22730f1422;p=haproxy.git IMPORT: uslz: do not replay the header-detection bytes on the raw deflate path When a stream turns out to be raw deflate and the first two bytes had to be accumulated into hdr_detect.buf across calls, those bytes are replayed to uslz_decode_block() from a local array, after which the code did: state->in_ptr = (const unsigned char *)compressed_data; state->in_top = state->in_ptr + compressed_size; That rewinds to the start of the caller's buffer, but the loop that filled hdr_detect.buf consumed from that same buffer, so the bytes just replayed were handed to the decoder a second time. Saving the real in_ptr across the replay fixes it; in_top never moved, and is saved with it only for symmetry. The problem happens whenever a raw deflate stream is fed in chunks of 1 or 2 bytes, since 3 or more takes the fast path at "in_top - in_ptr > 2" and never enters this branch. Feeding 5000 bytes of source through uslz_decode() one byte at a time failed with USLZ_DECODE_E_GEN_HUFF and no output at all: the duplicated bytes shift the bit stream, so the first dynamic block's code lengths no longer form a valid tree. gzip and zlib streams are unaffected, they leave this branch by a different location. The issue was spotted by Qwen-3.8-27B. This fix must be backported to 1.3. This is libslz upstream commit f052863a91aeb44021ed1bd52f680df45667496a --- diff --git a/src/uslz.c b/src/uslz.c index 84992f7ad..1e7a8f9e9 100644 --- a/src/uslz.c +++ b/src/uslz.c @@ -1434,6 +1434,8 @@ enum uslz_decode_ret uslz_decode(struct uslz_stream *state, * will be detected by tinflate_block(). */ unsigned char bytes[2]; + const unsigned char *save_ptr = state->in_ptr; + const unsigned char *save_top = state->in_top; int it = 0; if (state->hdr_detect.buf_len > 2) @@ -1452,9 +1454,15 @@ enum uslz_decode_ret uslz_decode(struct uslz_stream *state, state->in_top = bytes + state->hdr_detect.buf_len; res = uslz_decode_block(state); - /* restore original state */ - state->in_ptr = (const unsigned char *)compressed_data; - state->in_top = state->in_ptr + compressed_size; + /* Restore where the real input had got to, which is + * *not* the start of the caller's buffer: the loop + * that filled hdr_detect.buf above consumed from it, + * and those bytes have just been replayed from . + * Rewinding to would hand them to the + * decoder a second time. + */ + state->in_ptr = save_ptr; + state->in_top = save_top; goto next_block; }// else raw format without pending bytes