]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: mux-quic: prevent send corruption on buf realign
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 21 Oct 2026 13:47:59 +0000 (15:47 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 21 Oct 2026 15:49:01 +0000 (17:49 +0200)
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.

src/mux_quic.c

index 7c31594b2b09b18c95346113af9ce7f2f3011fc1..2c55a0cba025d92779de89a6112ed5b51e3f2b72 100644 (file)
@@ -1572,6 +1572,16 @@ static int qcc_decode_qcs(struct qcc *qcc, struct qcs *qcs)
        return ret;
 }
 
+/* Free <qcc> 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 <qcs> 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 <qcc> 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 <qcs> stream for emission of STREAM, STOP_SENDING or RESET_STREAM.
  * Set <urg> 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