]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: mux_quic: fix stream elasticity dec on qcm_init() failure
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Tue, 27 Oct 2026 15:09:28 +0000 (16:09 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 28 Oct 2026 10:06:00 +0000 (11:06 +0100)
On qcm_init() early failure, QCC is freed via qcc_release(). This
function always decrements reserved streams from the thread group pool
when stream elasticity is active for frontend connections.

This causes an issue as qcm_init() may fail before accounting the
committed streams: this is the case for <pacing_task> allocation
failure. Thread group counter is thus invalid after the extra decrement
and may cause an underflow and wrap.

Fix this by moving pacing task allocation later in qcm_init() so that
streams accounting is now performed before any failure path.

This must be backported up to 3.4.

src/mux_quic.c

index 94c4ea8909efd9387f0933a81a0999e53ef52ab9..769b6eb680037147e7844feccb8a218609c1824b 100644 (file)
@@ -4132,22 +4132,6 @@ static int qcm_init(struct connection *conn, struct proxy *prx,
 
        qcc->tx.buf_in_flight = 0;
 
-       if (qcc_is_pacing_active(conn)) {
-               quic_pacing_init(&qcc->tx.pacer, &conn->handle.qc->path->cc);
-               qcc->tx.paced_sent_ctr = 0;
-
-               /* Initialize pacing_task. */
-               qcc->pacing_task = task_new_here();
-               if (!qcc->pacing_task) {
-                       TRACE_ERROR("pacing task alloc failure", QMUX_EV_QCC_NEW);
-                       goto err;
-               }
-               qcc->pacing_task->process = qcc_pacing_task;
-               qcc->pacing_task->context = qcc;
-               qcc->pacing_task->expire = TICK_ETERNITY;
-               qcc->pacing_task->state |= TASK_F_WANTS_TIME;
-       }
-
        if (!conn_is_quic(conn)) {
                qcc->tx.qmux_buf = BUF_NULL;
                qcc->rx.qmux_buf = BUF_NULL;
@@ -4176,6 +4160,22 @@ static int qcm_init(struct connection *conn, struct proxy *prx,
                }
        }
 
+       if (qcc_is_pacing_active(conn)) {
+               quic_pacing_init(&qcc->tx.pacer, &conn->handle.qc->path->cc);
+               qcc->tx.paced_sent_ctr = 0;
+
+               /* Initialize pacing_task. */
+               qcc->pacing_task = task_new_here();
+               if (!qcc->pacing_task) {
+                       TRACE_ERROR("pacing task alloc failure", QMUX_EV_QCC_NEW);
+                       goto err;
+               }
+               qcc->pacing_task->process = qcc_pacing_task;
+               qcc->pacing_task->context = qcc;
+               qcc->pacing_task->expire = TICK_ETERNITY;
+               qcc->pacing_task->state |= TASK_F_WANTS_TIME;
+       }
+
        qcc->wait_event.tasklet = tasklet_new();
        if (!qcc->wait_event.tasklet) {
                TRACE_ERROR("tasklet alloc failure", QMUX_EV_QCC_NEW);