From: Amaury Denoyelle Date: Tue, 27 Oct 2026 15:09:28 +0000 (+0100) Subject: BUG/MINOR: mux_quic: fix stream elasticity dec on qcm_init() failure X-Git-Tag: v3.5-dev10~19 X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=9c8f5952032a705eb592ee4ab65c1b879ea0c864;p=haproxy.git BUG/MINOR: mux_quic: fix stream elasticity dec on qcm_init() failure 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 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. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index 94c4ea890..769b6eb68 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -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);