From faf3e9ac3a5df7258b0abbc06b0e0378617a18e5 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Thu, 7 May 2026 11:41:51 +0200 Subject: [PATCH] BUG/MEDIUM: mux-h2: Properly consume padding for DATA frames Since the commit 617592c9e ("MEDIUM: mux-h2: try to coalesce outgoing WINDOW_UPDATE frames"), padding of DATA frames is no longer consumed. Instead, this padidng is left in the demux buffer and used as the header of the next frame. Because all bytes of the padding must be zero, this lead to trigger a PROTOCOL_ERROR because haproxy erroneously thinks the peer sent a DATA frame for the stream-id 0. It is true for a padding of 9 bytes or more, but similar issues may be exprienced with smaller padding. Before the commit above, the padding was consumed in h2_process_demux to restore the H2_CS_FRAME_H state at the end of the while loop processing received frames. However, it seems a bit strange to deal with the padding at this stage, espcially because it is not obvious at all. So to fix the issue, the padding is now consumed at the end of h2_frt_transfer_data(), inside "end_tranfer" label. At the stage, we know all payload of the current DATA frame were consumed and only the padding is still there, if any. We must only take care to not consume more than available in the demux buffer. The padding may have been partially received. This patch should fix the issue #3354. It must be backported as far as 2.8. --- src/mux_h2.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/mux_h2.c b/src/mux_h2.c index 4d1bef4b2..c89d184a4 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -6521,8 +6521,22 @@ try_again: end_transfer: /* here we're done with the frame, all the payload (except padding) was - * transferred. + * transferred. So let's consume the padding now. + * + * The padding may not have been fully received, so we must take care to + * not consume more than avaiable and eventually retry later. */ + BUG_ON(h2c->dfl != h2c->dpl); + flen = b_data(&h2c->dbuf); + if (flen > h2c->dfl) + flen = h2c->dfl; + b_del(&h2c->dbuf, flen); + h2c->dfl -= flen; + h2c->dpl -= flen; + h2c->rcvd_c += flen; + h2c->rcvd_s += flen; + if (h2c->dfl) + goto fail; if (!(h2s->flags & H2_SF_BODY_TUNNEL) && (h2c->dff & H2_F_DATA_END_STREAM)) { /* no more data are expected for this message. This add the EOM @@ -6537,9 +6551,6 @@ try_again: } } - h2c->rcvd_c += h2c->dpl; - h2c->rcvd_s += h2c->dpl; - h2c->dpl = 0; h2c->st0 = H2_CS_FRAME_A; // send the corresponding window update htx_to_buf(htx, scbuf); TRACE_LEAVE(H2_EV_RX_FRAME|H2_EV_RX_DATA, h2c->conn, h2s);