From: Willy Tarreau Date: Mon, 12 Oct 2026 12:30:18 +0000 (+0200) Subject: BUG/MEDIUM: mux-h2: fix a possible crash on rxbuf allocation failure X-Git-Tag: v3.5-dev9~33 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=22eca878e9b9b9279ee0a3def827245960f63d75;p=haproxy.git BUG/MEDIUM: mux-h2: fix a possible crash on rxbuf allocation failure When receiving a large POST which spans multiple buffers, if the current rxbuf is full we try to allocate a new buffer. But if the allocation fails due to an enforced tune.buffers.limit for example, we leave and try to switch back the buffer to htx via htx_to_buf(htx, scbuf) while scbuf has become NULL, causing a crash. Let's just properly reset the htx pointer between each buffer switch so as not to attempt this operation. This is difficult to reproduce, but could be triggered once with tune.buffers.limit set to 6, with 1 to 3 pending HTTP/1 requests and a 60kB H2 POST. This was introduced in 3.1 by commit 4eb3ff1d3 ("MAJOR: mux-h2: make streams use the connection's buffers"), so the fix must be backported to 3.2. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index a393f8632..83b256f77 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -6598,7 +6598,7 @@ try_again: block = htx_free_data_space(htx); if (!block) { if (h2s_get_rxbuf(h2s)) - goto next_buffer; + goto switch_buffer; h2c->flags |= H2_CF_DEM_SFULL; TRACE_STATE("h2s rxbuf is full", H2_EV_RX_FRAME|H2_EV_RX_DATA|H2_EV_H2S_BLK, h2c->conn, h2s); @@ -6635,7 +6635,7 @@ try_again: if (sent < flen) { if (h2s_get_rxbuf(h2s)) - goto next_buffer; + goto switch_buffer; h2c->flags |= H2_CF_DEM_SFULL; TRACE_STATE("h2s rxbuf is full", H2_EV_RX_FRAME|H2_EV_RX_DATA|H2_EV_H2S_BLK, h2c->conn, h2s); @@ -6644,6 +6644,15 @@ try_again: goto try_again; + switch_buffer: + /* the current rxbuf is full and a new slot was assigned to the + * stream: release the current HTX message before switching to the + * next buffer, whose allocation may fail. + */ + htx_to_buf(htx, scbuf); + htx = NULL; + goto next_buffer; + end_transfer: /* here we're done with the frame, all the payload (except padding) was * transferred. So let's consume the padding now.