]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: h3: reject standalone FIN on invalid request/response
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 12 Nov 2026 14:40:59 +0000 (15:40 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 12 Nov 2026 16:40:22 +0000 (17:40 +0100)
HTTP/3 parser has a special code path to handle a standalone FIN.
However for a request stream there was no check to ensure that a valid
request/response was received before reporting the closure.

This would result in an empty HTX buffer with EOM set. On the MUX level,
QCS instance would remain accounted in QCC <nb_hreq> counter, which
could result in unnecessary memory consumption.

Fix this rejecting a request stream closed with a standalone FIN without
a valid request or response received. The stream read is aborted and
write is reset using error code H3_REQUEST_INCOMPLETE or
H3_MESSAGE_ERROR depending on the proxy side. This allows to quickly
release the QCS instance.

This must be backported up to 2.6.

src/h3.c

index f93ceb433ef258f39842988db7c7929ada0429c0..071d5629546d5f7012d6b03f234cfb444de0f36c 100644 (file)
--- a/src/h3.c
+++ b/src/h3.c
@@ -2019,11 +2019,23 @@ static ssize_t h3_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
                        goto done;
                }
 
-               if (quic_stream_is_bidi(qcs->id) && qcs_http_handle_standalone_fin(qcs, h3s->st_req == H3S_ST_REQ_TRAILERS)) {
-                       TRACE_ERROR("cannot set EOM", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
-                       qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1,
-                                     muxc_tevt_type_internal_err);
-                       goto err;
+               if (quic_stream_is_bidi(qcs->id)) {
+                       /* No header seen, HTTP message is malformed. */
+                       if (h3s->st_req == H3S_ST_REQ_BEFORE) {
+                               const int code = !conn_is_back(qcs->qcc->conn) ? H3_ERR_REQUEST_INCOMPLETE : H3_ERR_MESSAGE_ERROR;
+                               TRACE_ERROR("incomplete request/response closed with standalone FIN", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
+                               qcc_abort_stream_read(qcs, code);
+                               qcc_reset_stream(qcs, code, se_tevt_type_proto_err);
+                               qcc_report_glitch(qcs->qcc, 1);
+                               goto done;
+                       }
+
+                       if (qcs_http_handle_standalone_fin(qcs, h3s->st_req == H3S_ST_REQ_TRAILERS)) {
+                               TRACE_ERROR("cannot set EOM", H3_EV_RX_FRAME, qcs->qcc->conn, qcs);
+                               qcc_set_error(qcs->qcc, H3_ERR_INTERNAL_ERROR, 1,
+                                             muxc_tevt_type_internal_err);
+                               goto err;
+                       }
                }
 
                goto done;