]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: xprt_qmux: implement ->get_ssl_sock_ctx() to get the SSL laye
authorWilly Tarreau <w@1wt.eu>
Sat, 6 Jun 2026 08:32:36 +0000 (08:32 +0000)
committerWilly Tarreau <w@1wt.eu>
Mon, 8 Jun 2026 06:31:20 +0000 (08:31 +0200)
conn_get_ssl_sock_ctx() retrieves the ssl_sock_ctx of a connection by
calling conn->xprt->get_ssl_sock_ctx(). Only ssl_sock implements this
method, and it returns conn->xprt_ctx. This works because for every
existing XPRT combination the SSL layer is the topmost one: even
xprt_handshake (SOCKS4, PROXY, NetScaler CIP) is installed *below*
ssl_sock, so conn->xprt keeps pointing to ssl_sock.

Qmux changes this assumption: xprt_qmux is stacked *on top of* ssl_sock
and keeps the SSL layer as its lower layer to exchange the QUIC transport
parameters over the established TLS stream. During the qmux handshake,
conn->xprt therefore points to xprt_qmux, which does not implement
get_ssl_sock_ctx(), making conn_get_ssl_sock_ctx() return NULL for the
whole connection, affecting every caller that inspects the SSL layer
(sample fetches, logging, ssl_sock_infocbk(), ...).

The visible consequence was a crash: when the peer sends a TLS alert
during the qmux handshake, the SSL library calls ssl_sock_infocbk(),
which recovers a valid connection but a NULL ctx, rightfully triggering
the "BUG_ON(!ctx)" early in the function.

This patch implements xprt_qmux_get_ssl_sock_ctx() so that it returns
the ssl_sock_ctx of the lower layer when it is the SSL layer, just like
ssl_sock_get_ctx() does. conn_get_ssl_sock_ctx() then works again for
all callers while the qmux handshake is in progress. After the handshake,
conn->xprt is restored to the SSL layer so nothing else changes.

This should be backported to 3.4.

src/xprt_qmux.c

index 1fdbcd88bc9d5f8c6fe2cfe37f4fe96fbcf50cf9..4c29fad1f98e91bf881e5602cd4d73b2a517ea84 100644 (file)
@@ -350,6 +350,22 @@ static void xprt_qmux_close(struct connection *conn, void *xprt_ctx)
        pool_free(xprt_qmux_ctx_pool, ctx);
 }
 
+/* Retrieve the ssl_sock_ctx of the lower layer. Contrary to most XPRTs, QMux
+ * is stacked on top of the SSL layer (and not the other way around), so during
+ * the QMux handshake conn->xprt points to xprt_qmux. Without this delegation,
+ * conn_get_ssl_sock_ctx() would return NULL for any code inspecting the SSL
+ * layer of the connection (sample fetches, logging, info callback, ...) while
+ * the QMux handshake is in progress.
+ */
+static struct ssl_sock_ctx *xprt_qmux_get_ssl_sock_ctx(struct connection *conn)
+{
+       struct xprt_qmux_ctx *ctx = conn->xprt_ctx;
+
+       if (ctx && ctx->ops_lower == xprt_get(XPRT_SSL))
+               return ctx->ctx_lower;
+       return NULL;
+}
+
 static int xprt_qmux_get_alpn(const struct connection *conn, void *xprt_ctx,
                               const char **str, int *len)
 {
@@ -371,6 +387,7 @@ struct xprt_ops xprt_qmux = {
        .start     = xprt_qmux_start,
        .close     = xprt_qmux_close,
        .get_alpn  = xprt_qmux_get_alpn,
+       .get_ssl_sock_ctx = xprt_qmux_get_ssl_sock_ctx,
        .name      = "qmux",
 };