]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: h3: fix memory corruption on trailers parsing realign
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 19 Oct 2026 15:01:46 +0000 (17:01 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 20 Oct 2026 12:27:35 +0000 (14:27 +0200)
Since 3.0, intermediary QCS Tx buffer has been removed to reduce data
copy. Now, QCS buffer is shared with the transport layer. This brought
some constraints as buffer realign must not be performed if there is
data already sent not yet ACKed. Data encoding function has been adapted
to remove unsafe realign. However, this was not the case for trailers
encoding, which still perform a blind b_slow_realign().

If there is data still present in the buffer when parsing a trailers
frame, this operation will result in memory corruption when
acknowledgement will be received and incorrect buffer data deleted.

This patch fixes h3_resp_trailers_send() to ensure the correct realign
API is used, similar to what is already implemented for data. This is
based on qcc_realign_stream_txbuf() which is the safe method for content
realign.

This must be backported up to 3.0.

src/h3.c

index c49f8849d126eb1a59551097a9ed97760e6b609e..9ec89e750aff49f6195e38c40712b134dbc480ea 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -2899,7 +2899,7 @@ static int h3_resp_headers_send(struct qcs *qcs, struct htx *htx, char *fin)
 static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx, char *fin)
 {
        int err;
-       struct buffer headers_buf = BUF_NULL;
+       struct buffer headers_buf;
        struct buffer *res;
        struct http_hdr list[global.tune.max_http_hdr];
        struct htx_blk *blk;
@@ -2959,8 +2959,21 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx, char *fin)
                goto end;
        }
 
-       /* At least 9 bytes to store frame type + length as a varint max size */
-       if (b_room(res) < 9) {
+       while (1) {
+               b_reset(&headers_buf);
+               headers_buf = b_make(b_tail(res), b_contig_space(res), 0, 0);
+               /* At least 9 bytes to store frame type + length as a varint max size */
+               if (b_size(&headers_buf) > 9 || !b_space_wraps(res))
+                       break;
+               if (qcc_realign_stream_txbuf(qcs, res))
+                       break;
+       }
+
+       /* Not enough room for headers and at least one trailers byte, try to
+        * release the current buffer and allocate a new one. If not possible,
+        * stconn layer will subscribe on SEND.
+        */
+       if (b_size(&headers_buf) <= 9) {
                TRACE_STATE("not enough room for trailers frame", H3_EV_TX_FRAME|H3_EV_TX_HDR, qcs->qcc->conn, qcs);
                if (qcc_release_stream_txbuf(qcs))
                        goto end;
@@ -2969,9 +2982,6 @@ static int h3_resp_trailers_send(struct qcs *qcs, struct htx *htx, char *fin)
                goto start;
        }
 
-       /* Force buffer realignment as size required to encode headers is unknown. */
-       if (b_space_wraps(res))
-               b_slow_realign(res, trash.area, b_data(res));
        /* Start the headers after frame type + length */
        headers_buf = b_make(b_peek(res, b_data(res) + 9), b_contig_space(res) - 9, 0, 0);