]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: h3: fix parser desync on error with multiple frames
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 1 Jul 2026 14:19:26 +0000 (16:19 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 3 Jul 2026 06:12:00 +0000 (08:12 +0200)
On success, h3_rcv_buf() returns the number of parsed STREAM bytes which
are removed by the caller afterwards. A success value is mandatory so
that the underlying QUIC packet is acknowledged.

When H3 parser detects an error during HEADERS or DATA parsing, the
stream or the connetcion is flagged for closure. If there is remaining
frames, they are simply ignored and h3_rcv_buf() returns the remaining
input buffer size.

However, this value is wrong in case one or several frames were already
parsed before the invalid frame in the same h3_rcv_buf() invokation.
This instructs caller to only remove a subset of the data and parsing is
restarted on a random boundary. Most of the times this generates again a
new final yet invalid error, possibly overwriting a stream error with a
full connection closure.

This patch fixes the return value in case of an error during HEADERS or
DATA parsing by ensuring that total variable is always incremented
instead of being directly assigned.

This must be backported up to 2.8.

src/h3.c

index ed581e26a272dc31626b1043249cd195e8371f42..9876b6aaf0a2c4a8436342682a3a253973d11282 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -2113,12 +2113,12 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                  (h3s->err == H3_ERR_REQUEST_REJECTED) ? 0 : se_tevt_type_proto_err;
                qcc_abort_stream_read(qcs);
                qcc_reset_stream(qcs, h3s->err, tevt);
-               total = b_data(b);
+               total += b_data(b);
                goto done;
        }
        else if (h3c->err) {
                qcc_set_error(qcs->qcc, h3c->err, 1, muxc_tevt_type_proto_err);
-               total = b_data(b);
+               total += b_data(b);
                goto done;
        }
        else if (unlikely(ret < 0)) {