]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: don't always process pending handshakes on closed connections
authorWilly Tarreau <w@1wt.eu>
Mon, 29 Sep 2025 12:13:26 +0000 (14:13 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 1 Oct 2025 08:23:04 +0000 (10:23 +0200)
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

index e77d8eeef13b17309abdf7bf4ef16aad8c38727d..107732250f54d1b18657ca92cf94a24f4605eff8 100644 (file)
@@ -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);