]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: mux-h2: fix a possible crash on rxbuf allocation failure
authorWilly Tarreau <w@1wt.eu>
Mon, 12 Oct 2026 12:30:18 +0000 (14:30 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 12 Oct 2026 12:31:04 +0000 (14:31 +0200)
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.

src/mux_h2.c

index a393f8632b17240460fe466f32b3e47f247b8b87..83b256f77a2ddc9133ea39c99017b4b58161fd75 100644 (file)
@@ -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.