From: Amaury Denoyelle Date: Wed, 23 Sep 2026 13:34:28 +0000 (+0200) Subject: BUG/MEDIUM: mux_quic: account discarded data in connection flow control X-Git-Tag: v3.5-dev9~179 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=a6fe02e22177484766895aa8c77421b646ed8d46;p=haproxy.git BUG/MEDIUM: mux_quic: account discarded data in connection flow control Read data not yet consumed by the upper stream layer are discarded when a RESET_STREAM is received. This patch implements their accounting in the connection flow control. This feature is mandatory as it could result in discrepancies between each peers for the measured connection flow control. If this grows too big, the sender may be indefinitely blocked, forcing the opening of a new connection. This patch introduces a new function qcc_account_conn_fctl(), which is used to factorize the code already present during STREAM frames handling. This is now also called for RESET_STREAM frames. This must be backported up to 2.8, after a period of observation. --- diff --git a/src/mux_quic.c b/src/mux_quic.c index 86585999c..403acbfcd 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -1351,6 +1351,36 @@ static ncb_sz_t qcs_rx_avail_data(struct qcs *qcs) return b ? ncb_data(&b->ncb, 0) : 0; } +/* Account for in connection flow-control. If needed, a MAX_DATA + * frame is prepared for emission. + */ +static void qcc_account_for_conn_fctl(struct qcc *qcc, uint64_t bytes) +{ + struct quic_frame *frm; + + TRACE_ENTER(QMUX_EV_QCC_RECV, qcc->conn); + + qcc->lfctl.offsets_consume += bytes; + if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) { + TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn); + frm = qc_frm_alloc(QUIC_FT_MAX_DATA); + if (!frm) { + qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0, + muxc_tevt_type_internal_err); + goto out; + } + + qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init; + frm->max_data.max_data = qcc->lfctl.md; + + LIST_APPEND(&qcc->lfctl.frms, &frm->list); + tasklet_wakeup(qcc->wait_event.tasklet); + } + + out: + TRACE_LEAVE(QMUX_EV_QCC_RECV, qcc->conn); +} + /* Remove from current Rx buffer of stream. Flow-control * for received offsets may be allocated for the peer if needed. */ @@ -1422,23 +1452,7 @@ static void qcs_consume(struct qcs *qcs, uint64_t bytes, struct qc_stream_rxbuf } conn_fctl: - qcc->lfctl.offsets_consume += bytes; - if (qcc->lfctl.md - qcc->lfctl.offsets_consume < qcc->lfctl.md_init / 2) { - TRACE_DATA("increase conn credit via MAX_DATA", QMUX_EV_QCS_RECV, qcc->conn, qcs); - frm = qc_frm_alloc(QUIC_FT_MAX_DATA); - if (!frm) { - qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0, - muxc_tevt_type_internal_err); - return; - } - - qcc->lfctl.md = qcc->lfctl.offsets_consume + qcc->lfctl.md_init; - - frm->max_data.max_data = qcc->lfctl.md; - - LIST_APPEND(&qcs->qcc->lfctl.frms, &frm->list); - tasklet_wakeup(qcc->wait_event.tasklet); - } + qcc_account_for_conn_fctl(qcc, bytes); TRACE_LEAVE(QMUX_EV_QCS_RECV, qcc->conn, qcs); } @@ -2404,6 +2418,10 @@ int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err, uint64_t f qcs_free_rxbuf(qcs, b); } + qcc_account_for_conn_fctl(qcc, to_consume); + /* Ensure final size checks coherence for future STREAM frames. */ + qcs->rx.offset_max = final_size; + /* Remove stream from recv_list if present. */ LIST_DEL_INIT(&qcs->el_recv);