]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: quic-be: prevent use of MUX for 0-RTT sessions without secrets
authorFrederic Lecaille <flecaille@haproxy.com>
Mon, 17 Nov 2025 14:08:39 +0000 (15:08 +0100)
committerFrederic Lecaille <flecaille@haproxy.com>
Mon, 17 Nov 2025 14:40:24 +0000 (15:40 +0100)
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
src/quic_tx.c
src/xprt_quic.c

index 45915773bdf10616955e6db84d56a715c94d03e7..32d5982e52668a95fef24e244e30b57f27421400 100644 (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);
index e78df8a0559184b012347d79f8d580aa63a2059a..fa3c99bfeda53a3138cd593e271946399b76cc78 100644 (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);
index ca7d4e9f9c2903a379af70a3b12705f3d6283220..b177c6656735463434da1123b7601b43fc7ed426 100644 (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);