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.
This commit is contained in:
Frederic Lecaille 2025-11-17 15:08:39 +01:00
parent 0367227375
commit 37d01eea37
3 changed files with 22 additions and 1 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);