From 7ea80cc5b66e9a9e8e5618532f1c6deb93385761 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 29 Sep 2025 14:13:26 +0200 Subject: [PATCH] MEDIUM: ssl: don't always process pending handshakes on closed connections If a client aborts a pending SSL connection for whatever reason (timeout etc) and the listen queue is large, it may inflict a severe load to a frontend which will spend the CPU creating new sessions then killing the connection. This is similar to HTTP requests aborted just after being sent, except that asymmetric crypto is way more expensive. Unfortunately "option abortonclose" has no effect on this, because it only applies at a higher level. This patch ensures that handshakes being received on a frontend having "option abortonclose" set will be checked for a pending close, and if this is the case, then the connection will be aborted before the heavy calculations. The principle is to use recv(MSG_PEEK) to detect the end, and to destroy the pending handshake data before returning to the SSL library so that it cannot start computing, notices the error and stops. We don't do it without abortonclose though, because this can be used for health checks from other haproxy nodes or even other components which just want to see a handshake succeed. This is in relation with GH issue #3124. --- src/ssl_sock.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/ssl_sock.c b/src/ssl_sock.c index e77d8eeef..107732250 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -351,6 +351,7 @@ static int ha_ssl_read(BIO *h, char *buf, int size) struct ssl_sock_ctx *ctx; void *msg_control = NULL; size_t *msg_controllenp = NULL; + int detect_shutr; int ret; ctx = BIO_get_data(h); @@ -381,7 +382,18 @@ static int ha_ssl_read(BIO *h, char *buf, int size) } tmpbuf.data = 0; tmpbuf.head = 0; - ret = ctx->xprt->rcv_buf(ctx->conn, ctx->xprt_ctx, &tmpbuf, size, msg_control, msg_controllenp, 0); + + if (ctx->conn->flags & CO_FL_SSL_WAIT_HS && + !conn_is_back(ctx->conn) && + ((struct session *)ctx->conn->owner)->fe->options & PR_O_ABRT_CLOSE) + detect_shutr = 1; + else + detect_shutr = 0; + + ret = ctx->xprt->rcv_buf(ctx->conn, ctx->xprt_ctx, &tmpbuf, size, msg_control, msg_controllenp, detect_shutr ? CO_RFL_TRY_HARDER : 0); + if (detect_shutr && ctx->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) { + ret = -1; + } BIO_clear_retry_flags(h); if (ret == 0 && !(ctx->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH))) { BIO_set_retry_read(h); -- 2.47.3