sshd gets stuck: select() in packet_read_seqnr waits indefinitely

Darren Tucker dtucker at zip.com.au
Thu Mar 15 16:52:42 EST 2007


On Thu, Mar 15, 2007 at 02:22:13PM +1100, Darren Tucker wrote:
> On Wed, Mar 14, 2007 at 08:59:04PM -0600, Matt Day wrote:
> > What about the timeout handling in packet_write_wait? Do you really
> > want to proceed with the write() and then possibly loop back around
> > to the select() call?
> 
> Yeah, that's a problem.  On platforms that return EAGAIN rather than
> EWOULDBLOCK it'll just spin through packet_write_poll() repeatedly.
> 
> Probably best to capture the return from select() explicitly test for
> a timeout.

Like so (still untested):

Index: packet.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/packet.c,v
retrieving revision 1.146
diff -u -p -r1.146 packet.c
--- packet.c	17 Jan 2007 00:00:14 -0000	1.146
+++ packet.c	15 Mar 2007 05:51:19 -0000
@@ -136,6 +136,10 @@ static int server_side = 0;
 /* Set to true if we are authenticated. */
 static int after_authentication = 0;
 
+/* Set to the maximum time that we will wait to send or receive a packet */
+static struct timeval packet_wait_tv;
+static struct timeval *packet_wait_tvp = NULL;
+
 /* Session key information for Encryption and MAC */
 Newkeys *newkeys[MODE_MAX];
 static struct packet_state {
@@ -189,6 +193,21 @@ packet_set_connection(int fd_in, int fd_
 	}
 }
 
+void
+packet_set_timeout(int timeout, int count)
+{
+	if (timeout == 0 && count == 0) {
+		packet_wait_tvp = NULL;
+		return;
+	}
+	if (LONG_MAX / count < timeout)
+		packet_wait_tv.tv_sec = LONG_MAX;
+	else
+		packet_wait_tv.tv_sec = timeout * count;
+	packet_wait_tv.tv_usec = 0;
+	packet_wait_tvp = &packet_wait_tv;
+}
+
 /* Returns 1 if remote host is connected via socket, 0 if not. */
 
 int
@@ -888,7 +907,7 @@ packet_send(void)
 int
 packet_read_seqnr(u_int32_t *seqnr_p)
 {
-	int type, len;
+	int type, len, ret;
 	fd_set *setp;
 	char buf[8192];
 	DBG(debug("packet_read()"));
@@ -923,10 +942,15 @@ packet_read_seqnr(u_int32_t *seqnr_p)
 		FD_SET(connection_in, setp);
 
 		/* Wait for some data to arrive. */
-		while (select(connection_in + 1, setp, NULL, NULL, NULL) == -1 &&
+		while ((ret = select(connection_in + 1, setp, NULL, NULL,
+		    packet_wait_tvp)) == -1 &&
 		    (errno == EAGAIN || errno == EINTR))
 			;
-
+		if (ret == 0) {
+			logit("Connection to %.200s timed out",
+			    get_remote_ipaddr());
+			cleanup_exit(255);
+		}
 		/* Read data from the socket. */
 		len = read(connection_in, buf, sizeof(buf));
 		if (len == 0) {
@@ -1441,6 +1465,7 @@ void
 packet_write_wait(void)
 {
 	fd_set *setp;
+	int ret;
 
 	setp = (fd_set *)xcalloc(howmany(connection_out + 1, NFDBITS),
 	    sizeof(fd_mask));
@@ -1449,9 +1474,15 @@ packet_write_wait(void)
 		memset(setp, 0, howmany(connection_out + 1, NFDBITS) *
 		    sizeof(fd_mask));
 		FD_SET(connection_out, setp);
-		while (select(connection_out + 1, NULL, setp, NULL, NULL) == -1 &&
+		while ((ret = select(connection_out + 1, NULL, setp, NULL,
+		    packet_wait_tvp)) == -1 &&
 		    (errno == EAGAIN || errno == EINTR))
 			;
+		if (ret == 0) {
+			logit("Connection to %.200s timed out",
+			    get_remote_ipaddr());
+			cleanup_exit(255);
+		}
 		packet_write_poll();
 	}
 	xfree(setp);
Index: packet.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/packet.h,v
retrieving revision 1.47
diff -u -p -r1.47 packet.h
--- packet.h	26 Mar 2006 03:30:02 -0000	1.47
+++ packet.h	15 Mar 2007 04:01:39 -0000
@@ -21,6 +21,7 @@
 #include <openssl/bn.h>
 
 void     packet_set_connection(int, int);
+void     packet_set_timeout(int, int);
 void     packet_set_nonblocking(void);
 int      packet_get_connection_in(void);
 int      packet_get_connection_out(void);
Index: sshconnect.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/sshconnect.c,v
retrieving revision 1.171
diff -u -p -r1.171 sshconnect.c
--- sshconnect.c	23 Oct 2006 17:02:24 -0000	1.171
+++ sshconnect.c	15 Mar 2007 04:04:50 -0000
@@ -158,6 +158,8 @@ ssh_proxy_connect(const char *host, u_sh
 
 	/* Set the connection file descriptors. */
 	packet_set_connection(pout[0], pin[1]);
+	packet_set_timeout(options.server_alive_interval,
+	    options.server_alive_count_max);
 
 	/* Indicate OK return */
 	return 0;
@@ -386,6 +388,8 @@ ssh_connect(const char *host, struct soc
 
 	/* Set the connection. */
 	packet_set_connection(sock, sock);
+	packet_set_timeout(options.server_alive_interval,
+	    options.server_alive_count_max);
 
 	return 0;
 }
Index: sshd.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/sshd.c,v
retrieving revision 1.362
diff -u -p -r1.362 sshd.c
--- sshd.c	25 Feb 2007 09:37:22 -0000	1.362
+++ sshd.c	15 Mar 2007 03:58:53 -0000
@@ -1708,6 +1708,8 @@ main(int ac, char **av)
 	 * not have a key.
 	 */
 	packet_set_connection(sock_in, sock_out);
+	packet_set_timeout(options.client_alive_interval,
+	    options.client_alive_count_max);
 	packet_set_server();
 
 	/* Set SO_KEEPALIVE if requested. */

-- 
Darren Tucker (dtucker at zip.com.au)
GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4  37C9 C982 80C7 8FF4 FA69
    Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.


More information about the openssh-unix-dev mailing list