select() is not called properly in ssh_exchange_identification()

Damien Miller djm at mindrot.org
Wed May 31 12:51:00 AEST 2017


On Tue, 30 May 2017, Balu Gajjala wrote:

> Hi,
> 
> I found an issue with select() not called properly in the ssh_exchange_identification().
> 
> Variable "fdset" is passed as readfd, exceptionfd to the select().
> Select() should be called with independent fdset so we should have two different variables instead of reusing the same variable "fdset".
> Here is the code snippet. The reported issue is in line 566, 567.
> 
> Please let me know the procedure to create an official bug.

It's probably time to switch this to poll(2)

diff --git a/sshconnect.c b/sshconnect.c
index a9cc9f3..e6c8388 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -26,6 +26,7 @@
 #include <fcntl.h>
 #include <netdb.h>
 #include <paths.h>
+#include <poll.h>
 #include <signal.h>
 #include <pwd.h>
 #include <stdio.h>
@@ -322,12 +323,10 @@ static int
 timeout_connect(int sockfd, const struct sockaddr *serv_addr,
     socklen_t addrlen, int *timeoutp)
 {
-	fd_set *fdset;
-	struct timeval tv, t_start;
+	struct timeval t_start;
 	socklen_t optlen;
 	int optval, rc, result = -1;
-
-	gettimeofday(&t_start, NULL);
+	struct pollfd pfd;
 
 	if (*timeoutp <= 0) {
 		result = connect(sockfd, serv_addr, addrlen);
@@ -346,16 +345,18 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
 		goto done;
 	}
 
-	fdset = xcalloc(howmany(sockfd + 1, NFDBITS),
-	    sizeof(fd_mask));
-	FD_SET(sockfd, fdset);
-	ms_to_timeval(&tv, *timeoutp);
-
 	for (;;) {
-		rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
+		gettimeofday(&t_start, NULL);
+		pfd.fd = sockfd;
+		pfd.events = POLLIN;
+		rc = poll(&pfd, 1, *timeoutp);
+		ms_subtract_diff(&t_start, timeoutp);
 		if (rc != -1 || errno != EINTR)
 			break;
 	}
+	/* Socket may have connected by exhausted timeout anyway */
+	if (*timeoutp <= 0)
+		rc = 0;
 
 	switch (rc) {
 	case 0:
@@ -364,7 +365,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
 		break;
 	case -1:
 		/* Select error */
-		debug("select: %s", strerror(errno));
+		debug("poll: %s", strerror(errno));
 		break;
 	case 1:
 		/* Completed or failed */
@@ -387,8 +388,6 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
 		fatal("Bogus return (%d) from select()", rc);
 	}
 
-	free(fdset);
-
  done:
  	if (result == 0 && *timeoutp > 0) {
 		ms_subtract_diff(&t_start, timeoutp);
@@ -536,12 +535,9 @@ ssh_exchange_identification(int timeout_ms)
 	int connection_out = packet_get_connection_out();
 	u_int i, n;
 	size_t len;
-	int fdsetsz, remaining, rc;
-	struct timeval t_start, t_remaining;
-	fd_set *fdset;
-
-	fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
-	fdset = xcalloc(1, fdsetsz);
+	int remaining, rc;
+	struct timeval t_start;
+	struct pollfd pfd;
 
 	send_client_banner(connection_out, 0);
 
@@ -551,10 +547,9 @@ ssh_exchange_identification(int timeout_ms)
 		for (i = 0; i < sizeof(buf) - 1; i++) {
 			if (timeout_ms > 0) {
 				gettimeofday(&t_start, NULL);
-				ms_to_timeval(&t_remaining, remaining);
-				FD_SET(connection_in, fdset);
-				rc = select(connection_in + 1, fdset, NULL,
-				    fdset, &t_remaining);
+				pfd.fd = connection_in;
+				pfd.events = POLLIN;
+				rc = poll(&pfd, 1, remaining);
 				ms_subtract_diff(&t_start, &remaining);
 				if (rc == 0 || remaining <= 0)
 					fatal("Connection timed out during "
@@ -563,7 +558,7 @@ ssh_exchange_identification(int timeout_ms)
 					if (errno == EINTR)
 						continue;
 					fatal("ssh_exchange_identification: "
-					    "select: %s", strerror(errno));
+					    "poll: %s", strerror(errno));
 				}
 			}
 
@@ -594,7 +589,6 @@ ssh_exchange_identification(int timeout_ms)
 		debug("ssh_exchange_identification: %s", buf);
 	}
 	server_version_string = xstrdup(buf);
-	free(fdset);
 
 	/*
 	 * Check that the versions match.  In future this might accept


More information about the openssh-unix-dev mailing list