From 37d01eea37f090fe8a09a1ca0aa116d02ea26977 Mon Sep 17 00:00:00 2001 From: Frederic Lecaille Date: Mon, 17 Nov 2025 15:08:39 +0100 Subject: [PATCH] BUG/MEDIUM: quic-be: prevent use of MUX for 0-RTT sessions without secrets The QUIC backend crashes when its peer does not support 0-RTT. In this case, when the sessions are reused, no early-data level secrets are derived by the TLS stack. This leads to crashes from qc_send_mux() which does not suppose that both early-data level (qc->eel) and application level (qc->ael) cipher levels could be non initialized. To fix this: - prevent qc_send_mux() to send data if these two encryption level are not intialized. In this case it returns QUIC_TX_ERR_NONE; - avoid waking up the MUX from XPRT ->start() callback if the MUX is ready but without early-data level secrets to send them; - ensure the MUX is woken up by qc_ssl_do_handshake() after handshake completion if it is ready calling qc_notify_send() Thank you to @InputOutputZ for having reported this issue in GH #3188. No need to backport because QUIC backends is a current 3.3 development feature. --- src/quic_ssl.c | 8 ++++++++ src/quic_tx.c | 10 ++++++++++ src/xprt_quic.c | 5 ++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/quic_ssl.c b/src/quic_ssl.c index 45915773b..32d5982e5 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -1018,6 +1018,14 @@ int qc_ssl_do_hanshake(struct quic_conn *qc, struct ssl_sock_ctx *ctx) /* Wake up MUX after its creation. Operation similar to TLS+ALPN on TCP stack. */ qc->conn->mux->wake(qc->conn); } + else { + /* Wake up upper layer if the MUX is alreay initialized. + * This is the case when the MUX was started for a 0-RTT session + * but without early-data secrets to send them (when the server + * does not support 0-RTT). + */ + qc_notify_send(qc); + } } else { TRACE_PROTO("could not start the mux", QUIC_EV_CONN_IO_CB, qc); diff --git a/src/quic_tx.c b/src/quic_tx.c index e78df8a05..fa3c99bfe 100644 --- a/src/quic_tx.c +++ b/src/quic_tx.c @@ -521,6 +521,16 @@ enum quic_tx_err qc_send_mux(struct quic_conn *qc, struct list *frms, TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc); + if (!qel) { + BUG_ON(!qc_is_back(qc) || + !(__objt_server(qc->conn->target)->ssl_ctx.options & SRV_SSL_O_EARLY_DATA)); + /* This may happen when 0-RTT is enabled without early-data level secrets. + * This always occurs when the server peer does not support 0-RTT. + */ + TRACE_DEVEL("cannot send at 0-RTT level", QUIC_EV_CONN_TXPKT, qc); + return QUIC_TX_ERR_NONE; + } + if (qc->conn->flags & CO_FL_SOCK_WR_SH) { qc->conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH; TRACE_DEVEL("connection on error", QUIC_EV_CONN_TXPKT, qc); diff --git a/src/xprt_quic.c b/src/xprt_quic.c index ca7d4e9f9..b177c6656 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -209,8 +209,11 @@ static int qc_xprt_start(struct connection *conn, void *ctx) /* Schedule quic-conn to ensure post handshake frames are emitted. This * is not done for 0-RTT as xprt->start happens before handshake * completion. + * Note that, when 0-RTT is enabled for backend connections, it is + * possible that the ealy-data secrets could not be derived. This is the + * case when the server does not support 0-RTT. */ - if ((qc_is_back(qc) && !qc_is_conn_ready(qc)) || + if ((qc_is_back(qc) && (!qc_is_conn_ready(qc) || !qc->eel)) || (qc->flags & QUIC_FL_CONN_NEED_POST_HANDSHAKE_FRMS)) tasklet_wakeup(qc->wait_event.tasklet);