From: Amaury Denoyelle Date: Mon, 26 Oct 2026 10:21:32 +0000 (+0100) Subject: BUG/MAJOR: mux_quic: remove data not yet sent on reset stream X-Git-Tag: v3.5-dev10~23 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=e682b1e9c276bb60d7ca1790348f401ccfc076c1;p=haproxy.git BUG/MAJOR: mux_quic: remove data not yet sent on reset stream When a stream is reset, it is expected that no more data will be emitted. Thus, prepared data not yet sent is subtracted from the connection flow control. However, nothing is performed for the data itself, which may have important side effects. Most of the time, this issue is invisible as RESET_STREAM frame is finally emitted, QCS instance is fully closed and removed from the send list, ready for its deletion. However, an issue is possible if the read channel is not yet closed and output has been blocked for whatever reason. If output is unblocked before the read channel closure, QCS may emit new data STREAM frames, triggering a BUG_ON() on stream flow control in qcm_ctrl_send(). This assertion reveals a possible violation of RFC 9000 3.3 (STREAM frame sent in "Reset Sent" state) and of the final size already advertised by RESET_STREAM frame. The proper fix to this issue is to also clear prepared data when stream is reset, along with the flow control subtraction. This guarantees that no more STREAM frames will be emitted after stream reset, as any potential snd_buf stream layer callback will drain content. This must be backported to all stable releases. Note that qcs_tx_buf() is available since 3.4, needs adjustment for previous releases. Many thanks to "Sungho Park (@gigio1023)" for its report and analysis of the issue. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index a7925ddb9..bf538b3cb 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -1815,12 +1815,20 @@ void qcc_reset_stream(struct qcs *qcs, int err, int tevt) if (conn_is_back(qcs->qcc->conn)) qcs_idle_open(qcs); + /* Cancel emission of data not yet sent: clears not yet sent buffered + * data and subtract this amount from the connection flow control. + */ if (diff) { const int soft_blocked = qfctl_sblocked(&qcc->tx.fc); + struct buffer *out = qcs_tx_buf(qcs); /* Soft offset cannot be inferior to real one. */ BUG_ON(qcc->tx.fc.off_soft - diff < qcc->tx.fc.off_real); + /* Remove prepared data not yet sent. */ + b_sub(out, diff); + qcs->flags &= ~QC_SF_FIN_STREAM; + /* Subtract to conn flow control data amount prepared on stream not yet sent. */ qcc->tx.fc.off_soft -= diff; if (soft_blocked && !qfctl_sblocked(&qcc->tx.fc))