From: Christopher Faulet Date: Fri, 11 Sep 2026 15:43:13 +0000 (+0200) Subject: BUG/MINOR: mux-h2: Reject trailers received on a tunneled stream X-Git-Tag: v3.5-dev8~175 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=3a2f727536dc1b1cf12701b1af2508c66456c8df;p=haproxy.git BUG/MINOR: mux-h2: Reject trailers received on a tunneled stream For a tunneled stream, the end of the HTTP message is reported just after the headers. It is an "early" EOM: from the HTTP point of view, the message is finished and only tunneled data may be exchanged then. However, a HEADERS frame received after the initial one on such a stream was still handled as a trailers block. The trailers were thus pushed in an HTTP message already ended, leading to an invalid HTX message where blocks are inserted after the end of the message. The stream is then aborted when the outgoing multiplexer handles the unexpected block. So a client is able to kill its own tunnel by sending a bogus HEADERS frame. Such a frame is now rejected with a stream error (PROTOCOL_ERROR). The test is performed in h2c_dec_hdrs(), at the only place where a HEADERS frame is identified as a trailers block. This way, both directions are covered. The aborted tunnel attempts are also covered because the early EOM was reported in that case too. This patch should be backported to all stable versions. Note that h2c_report_glitch() only exists since 3.0. The corresponding line must be removed for older versions. --- diff --git a/src/mux_h2.c b/src/mux_h2.c index ab8628403..21cd83b3e 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -6475,6 +6475,29 @@ next_frame: trailers: /* This is the last HEADERS frame hence a trailer */ + if ((*flags & H2_SF_BODY_TUNNEL) || + (!(h2c->flags & H2_CF_IS_BACK) && (*flags & H2_SF_TUNNEL_ABRT))) { + /* No trailers are expected on a tunneled stream. The end of the + * HTTP message was already reported, just after the headers, so + * only tunneled data may be received now. + * + * On the request side, this remains true when the tunnel attempt + * was aborted, because the early end of the message was already + * reported for the CONNECT request. But not on the response + * side: an aborted tunnel means the response is a regular one, + * with a payload and thus possibly with trailers. In this case, + * no early end of the message was reported. + * + * Note: Regarding the RFC, it is not strickly speaking + * forbidden. But internally, we don't know how to handle such + * frames. So better to reject them. + */ + h2c_report_glitch(h2c, 1, "rcvd trailers on a tunneled stream"); + TRACE_STATE("rcvd trailers on a tunneled stream", H2_EV_RX_FRAME|H2_EV_RX_HDR|H2_EV_H2S_ERR|H2_EV_PROTO_ERR, h2c->conn); + HA_ATOMIC_INC(&h2c->px_counters->strm_proto_err); + goto fail; + } + if (!(h2c->dff & H2_F_HEADERS_END_STREAM)) { /* It's a trailer but it's missing ES flag */ h2c_report_glitch(h2c, 1, "missing EH on trailers frame");