]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: connection: don't leak the fd of an accepted connection
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 22 Oct 2026 08:12:11 +0000 (10:12 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 23 Oct 2026 17:36:57 +0000 (19:36 +0200)
When session_accept_fd() fails in conn_prepare(), e.g. because the SSL
connection limit (maxsslconn) is reached, conn_ctrl_init() hasn't run yet
and CO_FL_CTRL_READY isn't set. conn_ctrl_close() then cannot close the fd,
leaving it open. Additional incoming connections on the same listener keep
accumulating leaked fds, even after the SSL connections are released, and
may ultimately exhaust maxsock, also preventing non-SSL frontends from
accepting.

Fix this by closing the fd in conn_ctrl_close() when it hasn't been
registered yet, unless the connection is fdless.

Many thanks to Adam Crawford for the report.

This should be backported to all supported versions.

include/haproxy/connection.h

index e51f2b42371662db3bfd32f78d61500ceb1d9542..26dd404ce700aa6161f8bd28ca0cd14ec9a7ff9f 100644 (file)
@@ -196,13 +196,26 @@ static inline void conn_ctrl_init(struct connection *conn)
  */
 static inline void conn_ctrl_close(struct connection *conn)
 {
-       if (!conn->xprt && (conn->flags & CO_FL_CTRL_READY)) {
+       if (conn->xprt)
+               return;
+
+       if (conn->flags & CO_FL_CTRL_READY) {
                if ((conn->flags & (CO_FL_WANT_DRAIN | CO_FL_SOCK_RD_SH)) == CO_FL_WANT_DRAIN)
                        conn_ctrl_drain(conn);
                conn->flags &= ~CO_FL_CTRL_READY;
                if (conn->ctrl->ctrl_close)
                        conn->ctrl->ctrl_close(conn);
        }
+       else if (!(conn->flags & CO_FL_FDLESS) && conn->handle.fd != DEAD_FD_MAGIC) {
+               /* the connection got an accepted FD temporarily assigned but
+                * it was never inserted, thus it's not known from the fdtab.
+                * As soon as it's closed, it cannot be touched anymore as it
+                * may be re-assigned to another thread.
+                */
+               close(conn->handle.fd);
+               conn->handle.fd = DEAD_FD_MAGIC;
+       }
+
 }
 
 /* If the connection still has a transport layer, then call its close() function