slz_encode() has no output buffer bound parameter; it writes
unconditionally to strm->outbuf. The function comment claims "output
result may be up to 5 bytes larger than the input", but this only
holds when the bit9>=52 heuristic at slz.c:634 forces a switch to
stored blocks. An attacker can defeat the heuristic by interleaving
short (4-byte) matches between runs of <= 51 bytes >= 144, which are
encoded with 9-bit fixed Huffman codes.

With this pattern, ~51 9-bit literals + one ~16-bit match per 55
input bytes = ~8.6 bits/byte. Pure literals >= 144 with no matches
expand by 12.5%. The comp_http_payload input cap was b_size(&trash)
with no headroom, so a 16336-byte HTX DATA block of crafted data
expands to ~17600+ bytes into a 16384-byte trash buffer.

The trash buffer is a thread-local heap allocation (chunk.c:189
my_realloc2). The overflow corrupts adjacent heap objects with
deflate-encoded bytes whose structure the attacker controls.

With "compression direction request" enabled, this is directly
client-triggered. Otherwise it requires a malicious backend.

Also handle htx_replace_blk_value() returning NULL on the same
expansion path: when delta > htx_free_space, the function returns
NULL but htx_get_next_blk(htx, NULL) was called anyway, computing
a wild pointer from (htx->blocks + htx->size) / 8 - 1.

The zlib path (USE_ZLIB) is not affected; it correctly passes
avail_out. Only USE_SLZ builds (the default) are vulnerable.

Cap input to 7/8 of remaining trash room minus 32 bytes for the
gzip header and flush trailer. This is conservative but safe.

This must be backported to all supported versions.
---
 src/flt_http_comp.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c
index 6e15cb7e9181..46ec6851fbe6 100644
--- a/src/flt_http_comp.c
+++ b/src/flt_http_comp.c
@@ -316,9 +316,17 @@ comp_http_payload(struct stream *s, struct filter *filter, 
struct http_msg *msg,
                                        last = 0;
                                        v.len = len;
                                }
-                               if (v.len > b_size(&trash)) {
+                               /* SLZ has no output bound and can expand by up 
to
+                                * ~12.5% (9-bit fixed-Huffman literals for 
bytes
+                                * >= 144). The slz.c comment claiming "5 bytes
+                                * larger than the input" only holds when its
+                                * bit9>=52 heuristic fires, which an attacker 
can
+                                * defeat by interleaving short matches. Reserve
+                                * 1/8 headroom plus ~32 bytes for 
headers/flush.
+                                */
+                               if (v.len > b_room(&trash) - (b_room(&trash) >> 
3) - 32) {
                                        last = 0;
-                                       v.len = b_size(&trash);
+                                       v.len = b_room(&trash) - 
(b_room(&trash) >> 3) - 32;
                                }
 
                                ret = htx_compression_buffer_add_data(st, 
v.ptr, v.len, &trash, dir);
@@ -330,6 +338,8 @@ comp_http_payload(struct stream *s, struct filter *filter, 
struct http_msg *msg,
                                        next = htx_remove_blk(htx, blk);
                                else {
                                        blk = htx_replace_blk_value(htx, blk, 
v, ist2(b_head(&trash), b_data(&trash)));
+                                       if (!blk)
+                                               goto error;
                                        next = htx_get_next_blk(htx, blk);
                                }
 
-- 
2.53.0



Reply via email to