From: Amaury Denoyelle Date: Wed, 21 Oct 2026 13:47:59 +0000 (+0200) Subject: BUG/MINOR: mux-quic: prevent send corruption on buf realign X-Git-Tag: v3.5-dev10~78 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=0b8c835174881301e9955330d2c06529ca161c23;p=haproxy.git BUG/MINOR: mux-quic: prevent send corruption on buf realign For QUIC MUX emission, frames prepared for sending are not rebuilt each time qcc_io_send() is called. This was first implemented for pacing and the principle was generalized by the following patch, so the frame list is now preserved if emission is interrupted on pacing, transient transport error or flow control limitation. 14710b5e6bf76834343d58db22e00b72590b16fe MEDIUM/OPTIM: mux-quic: do not rebuild frms list on every send It is necessary to reset the frames list if some action occured during an emission in several steps, for example when new data from upper layer is available for sending or flow control limit is extended. Prepared frames directly point to each QCS Tx buffer. Thus if buf realign is performed, related frame will point to an incorrect location in the buffer, resulting in an invalid data transfer. Note that this issue is most of the time adverted by the fact that frames list is resetted via qcc_send_stream() which most of the time directly follow a realign operation. Fix this by refreshing the frames list also during Tx buf realign. This is simply implemented via a call to qcc_clear_frms(). It is currently not possible to only clear the single QCS frame, as next emission would only use already generated frames from other streams. This must be backported up to 3.2. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index 7c31594b2..2c55a0cba 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -1572,6 +1572,16 @@ static int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs) return ret; } +/* Free STREAM frames in Tx list. */ +static void qcc_clear_frms(struct qcc *qcc) +{ + TRACE_STATE("resetting STREAM frames list", QMUX_EV_QCC_SEND, qcc->conn); + while (!LIST_ISEMPTY(&qcc->tx.frms)) { + struct quic_frame *frm = LIST_ELEM(qcc->tx.frms.n, struct quic_frame *, list); + qc_frm_free(conn_is_quic(qcc->conn) ? qcc->conn->handle.qc : NULL, &frm); + } +} + /* Allocate if needed and retrieve stream buffer for data reception. * * Returns buffer pointer. May be NULL on allocation failure. @@ -1698,6 +1708,11 @@ int qcc_realign_stream_txbuf(const struct qcs *qcs, struct buffer *out) { if (qcs_prep_bytes(qcs) == b_data(out)) { b_slow_realign(out, trash.area, b_data(out)); + /* TODO only the QCS owned STREAM frame should be removed, + * however for now there is no mechanism to retry emission + * without clearing the whole list. + */ + qcc_clear_frms(qcs->qcc); return 0; } @@ -1748,16 +1763,6 @@ static void qcc_notify_fctl(struct qcc *qcc) } } -/* Free STREAM frames in Tx list. */ -static void qcc_clear_frms(struct qcc *qcc) -{ - TRACE_STATE("resetting STREAM frames list", QMUX_EV_QCC_SEND, qcc->conn); - while (!LIST_ISEMPTY(&qcc->tx.frms)) { - struct quic_frame *frm = LIST_ELEM(qcc->tx.frms.n, struct quic_frame *, list); - qc_frm_free(conn_is_quic(qcc->conn) ? qcc->conn->handle.qc : NULL, &frm); - } -} - /* Register stream for emission of STREAM, STOP_SENDING or RESET_STREAM. * Set to true if stream should be emitted in priority. This is useful * when sending STOP_SENDING or RESET_STREAM, or for emission on an application