From: Amaury Denoyelle Date: Wed, 23 Sep 2026 12:32:30 +0000 (+0200) Subject: BUG/MAJOR: mux_quic: fix leak on RESET_STREAM reception X-Git-Tag: v3.5-dev8~117 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=b7e0aa231aa7af214c97f9540f7bba8498898b11;p=haproxy.git BUG/MAJOR: mux_quic: fix leak on RESET_STREAM reception On QUIC MUX, counter serves to account for the currently active requests in progress. While this number is non null, the connection is considered active and cannot be released. The counter is decremented only when the QCS instance is finally freed. This guarantees that no truncated request could happen. An issue happened on RESET_STREAM frame reception as the first frame for a new QCS instance. In this case, is incremented. No progress will occur as the upper stream layer is not instantiated, and QCS instance will remains half-closed, which prevents its liberation. The first issue is that it causes a leak of QCS during the connection lifetime. Worst, this may prevent the whole connection closure even if idle timeout as fired : this is because qcc_is_dead() checks first before regarding transport layer status. Note that this only happens if QUIC MUX timeout is not yet activated, which is the case if qcc_io_cb() has not yet been executed. This patch fixes this situation by improving RESET_STREAM receive handler. Now, an extra check is performed to detect if the frame has been received before the upper stream layer init. If true, QCS instance is closed locally and immediately scheduled for purge, which ensures that counter will be decremented as expected. This must be backported up to 2.8. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index a8a552cb6..b5da163cf 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -2360,6 +2360,18 @@ int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t f /* Remove stream from recv_list if present. */ LIST_DEL_INIT(&qcs->el_recv); + /* Check if RESET_STREAM received before stream layer initialization. + * If true, prepare QCS purgeing immediately. + */ + if (!qcs_sc(qcs) && !(qcs->flags & QC_SF_DETACH)) { + qcs_close_local(qcs); + + BUG_ON(LIST_INLIST(&qcs->el_send)); + TRACE_STATE("add stream in purg_list", QMUX_EV_QCC_RECV|QMUX_EV_QCS_RECV, qcc->conn, qcs); + LIST_APPEND(&qcs->qcc->purg_list, &qcs->el_send); + tasklet_wakeup(qcs->qcc->wait_event.tasklet); + } + out: if (qcc->glitches != prev_glitches && !(qcc->flags & QC_CF_IS_BACK)) session_add_glitch_ctr(qcc->conn->owner, qcc->glitches - prev_glitches);