[openssh-commits] [openssh] 02/02: upstream: implement EMFILE mitigation for ssh-agent: remember the

git+noreply at mindrot.org git+noreply at mindrot.org
Fri May 11 13:56:50 AEST 2018


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit b2140a739be4c3b43cc1dc08322dca39a1e39d20
Author: djm at openbsd.org <djm at openbsd.org>
Date:   Fri May 11 03:38:51 2018 +0000

    upstream: implement EMFILE mitigation for ssh-agent: remember the
    
    fd rlimit and stop accepting new connections when it is exceeded (with some
    grace). Accept is resumed when enough connections are closed.
    
    bz#2576. feedback deraadt; ok dtucker@
    
    OpenBSD-Commit-ID: 6a85d9cec7b85741961e7116a49f8dae777911ea
---
 ssh-agent.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 49 insertions(+), 11 deletions(-)

diff --git a/ssh-agent.c b/ssh-agent.c
index cd028913..d8a8260f 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.230 2018/04/10 00:10:49 djm Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.231 2018/05/11 03:38:51 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -886,10 +886,10 @@ handle_conn_write(u_int socknum)
 }
 
 static void
-after_poll(struct pollfd *pfd, size_t npfd)
+after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds)
 {
 	size_t i;
-	u_int socknum;
+	u_int socknum, activefds = npfd;
 
 	for (i = 0; i < npfd; i++) {
 		if (pfd[i].revents == 0)
@@ -909,18 +909,30 @@ after_poll(struct pollfd *pfd, size_t npfd)
 		/* Process events */
 		switch (sockets[socknum].type) {
 		case AUTH_SOCKET:
-			if ((pfd[i].revents & (POLLIN|POLLERR)) != 0)
-				handle_socket_read(socknum);
+			if ((pfd[i].revents & (POLLIN|POLLERR)) == 0)
+				break;
+			if (npfd > maxfds) {
+				debug3("out of fds (active %u >= limit %u); "
+				    "skipping accept", activefds, maxfds);
+				break;
+			}
+			if (handle_socket_read(socknum) == 0)
+				activefds++;
 			break;
 		case AUTH_CONNECTION:
 			if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 &&
 			    handle_conn_read(socknum) != 0) {
-				close_socket(&sockets[socknum]);
-				break;
+				goto close_sock;
 			}
 			if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 &&
-			    handle_conn_write(socknum) != 0)
+			    handle_conn_write(socknum) != 0) {
+ close_sock:
+				if (activefds == 0)
+					fatal("activefds == 0 at close_sock");
 				close_socket(&sockets[socknum]);
+				activefds--;
+				break;
+			}
 			break;
 		default:
 			break;
@@ -929,7 +941,7 @@ after_poll(struct pollfd *pfd, size_t npfd)
 }
 
 static int
-prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp)
+prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds)
 {
 	struct pollfd *pfd = *pfdp;
 	size_t i, j, npfd = 0;
@@ -958,6 +970,16 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp)
 	for (i = j = 0; i < sockets_alloc; i++) {
 		switch (sockets[i].type) {
 		case AUTH_SOCKET:
+			if (npfd > maxfds) {
+				debug3("out of fds (active %zu >= limit %u); "
+				    "skipping arming listener", npfd, maxfds);
+				break;
+			}
+			pfd[j].fd = sockets[i].fd;
+			pfd[j].revents = 0;
+			pfd[j].events = POLLIN;
+			j++;
+			break;
 		case AUTH_CONNECTION:
 			pfd[j].fd = sockets[i].fd;
 			pfd[j].revents = 0;
@@ -1058,6 +1080,7 @@ main(int ac, char **av)
 	int timeout = -1; /* INFTIM */
 	struct pollfd *pfd = NULL;
 	size_t npfd = 0;
+	u_int maxfds;
 
 	ssh_malloc_init();	/* must be called before any mallocs */
 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
@@ -1069,6 +1092,9 @@ main(int ac, char **av)
 
 	platform_disable_tracing(0);	/* strict=no */
 
+	if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
+		fatal("%s: getrlimit: %s", __progname, strerror(errno));
+
 #ifdef WITH_OPENSSL
 	OpenSSL_add_all_algorithms();
 #endif
@@ -1165,6 +1191,18 @@ main(int ac, char **av)
 		printf("echo Agent pid %ld killed;\n", (long)pid);
 		exit(0);
 	}
+
+	/*
+	 * Minimum file descriptors:
+	 * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) +
+	 * a few spare for libc / stack protectors / sanitisers, etc.
+	 */
+#define SSH_AGENT_MIN_FDS (3+1+1+1+4)
+	if (rlim.rlim_cur < SSH_AGENT_MIN_FDS)
+		fatal("%s: file descriptior rlimit %lld too low (minimum %u)",
+		    __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS);
+	maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS;
+
 	parent_pid = getpid();
 
 	if (agentsocket == NULL) {
@@ -1284,7 +1322,7 @@ skip:
 	platform_pledge_agent();
 
 	while (1) {
-		prepare_poll(&pfd, &npfd, &timeout);
+		prepare_poll(&pfd, &npfd, &timeout, maxfds);
 		result = poll(pfd, npfd, timeout);
 		saved_errno = errno;
 		if (parent_alive_interval != 0)
@@ -1295,7 +1333,7 @@ skip:
 				continue;
 			fatal("poll: %s", strerror(saved_errno));
 		} else if (result > 0)
-			after_poll(pfd, npfd);
+			after_poll(pfd, npfd, maxfds);
 	}
 	/* NOTREACHED */
 }

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list