On Wed, Sep 23, 2026 at 06:20:18PM +0900, ygkat wrote:
> Hi Amaury,
> Thanks for taking a look.
> > I'm not sure to agree yet with the proposed fix as this could have
> > unwanted consequences, most notably with the backend support (which
> > the proposed patch does not support here). However, a localized fix
> > in qcc_recv_reset_stream() is at least mandatory here.
> That's a fair point, and honestly the reason our patch doesn't cover
> qcc_be_is_reusable()/backend semantics is simply that we don't run any
> QUIC/HTTP3 backends ourselves, so we hadn't considered that case at
> all.
> We're currently carrying a live leak in production from this (around
> 1.22M stuck connections on one node before we restarted it), so we're
> planning to apply both what we submitted before (the qcc_is_dead()
> reorder, frontend-only as submitted) and the change below internally,
> regardless of the outcome of the wider upstream discussion. Since
> we don't run QUIC/HTTP3 backends, the backend concern you raised
> doesn't affect our own deployment either way.
I understand your concern given the impact of the leak on your side.
> The change below is the qcc_recv_reset_stream() timeout refresh you
> mentioned as at least mandatory. It isn't guarded by conn_is_back() at
> all, so it already applies uniformly to both sides. For reference,
> this is what we have (against current master):
> int qcc_recv_reset_stream(struct qcc *qcc, uint64_t id, uint64_t err,
> uint64_t final_size)
> {
> ...
> qcs->flags |= QC_SF_SIZE_KNOWN|QC_SF_RECV_RESET;
> qcs_close_remote(qcs);
> + /* Unlike STREAM frame handling and qcc_recv_stop_sending(), this
> + * function had no qcc_refresh_timeout() call of its own. A stream
> + * reset before its request is complete leaves nb_hreq stuck and
> + * never emits any TX data, so qcc->task could otherwise be left
> + * with a stale expire computed once at qmux_init() time and never
> + * queued. qcc_refresh_timeout() already checks qcc_may_expire()
> + * (based on nb_sc, not nb_hreq) and recomputes from opening_list,
> + * so it is safe to call unconditionally here.
> + */
> + qcc_refresh_timeout(qcc);
> +
> /* RFC 9000 3.2. Receiving Stream States
> *
> * An
> * implementation MAY interrupt delivery of stream data, discard
> any
> * data that was not consumed, and signal the receipt of the
> * RESET_STREAM.
> ...
The more I dig, and the more I considered this fix not quite enough. The
code must be strengthen to ensure we never run without timeout unless a
stream is indeed in progress. It will be probably a good measure first
to add an unconditionnal call to qcc_refresh_timeout(qcc) on qcm_init()
to activate the timeout as soon as the MUX is instantiated. Also, I'm
concerned that nb_hreq is not immediately decremented on RESET_STREAM
reception as this means the HTTP request will never be completed.
Another fix is probably necessary here as well.
> Wanted to check first in case you see any problem with us applying
> both of these internally ahead of whatever ends up merged upstream, or
> any risk specific to a frontend-only QUIC deployment that we might be
> missing beyond the backend point you already raised.
I think the patch on qcc_recv_reset_stream() is safe so I encourage you
to run with it. The reorder change in qcc_is_dead() is definitely larger
and could trigger truncated streams on QUIC frontend side. If you decide
to use it, it would be a good idea to check that the number of stream
errors is not increased.
I'll notify you when the final patches will be merged on our side.
Thanks,
--
Amaury Denoyelle