[PATCH]: Patch to fix hang on exit bug under Linux and add optional exit delay

John Bowman bowman at math.ualberta.ca
Wed Nov 14 11:31:43 EST 2001


The hang-on-exit bug still hasn't been fixed in OpenSSH-3.0p1... :-(

Here again is the patch to fix this bug under Linux, updated for OpenSSH-3.0p1.
This has been exhaustively tested for six months now.  It also add an exit
delay option that can be useful.

The patch does not lead to data loss under Linux. 

Please see the Secure NFS page (SNFS) for further details:

http://www.math.ualberta.ca/imaging/snfs/

-- John Bowman
University of Alberta

diff -ur openssh-3.0p1/channels.c openssh-3.0p1J/channels.c
--- openssh-3.0p1/channels.c	Thu Oct 11 19:35:05 2001
+++ openssh-3.0p1J/channels.c	Tue Nov 13 16:02:32 2001
@@ -1553,8 +1553,18 @@
 		c = channels[i];
 		if (c == NULL)
 			continue;
-		if (ftab[c->type] != NULL)
+		if (ftab[c->type] != NULL) {
+			if(c->istate == CHAN_INPUT_OPEN && c->rfd == -1) {
+			  int type=c->type;
+			  c->type=SSH_CHANNEL_CLOSED;
+			  if(channel_find_open() == -1)
+			    shutdown(packet_get_connection_out(),
+				     SHUT_RDWR);
+			  c->type=type;
+			  continue;
+			}
 			(*ftab[c->type])(c, readset, writeset);
+		}
 		channel_garbage_collect(c);
 	}
 }
diff -ur openssh-3.0p1/channels.h openssh-3.0p1J/channels.h
--- openssh-3.0p1/channels.h	Thu Oct 11 19:35:06 2001
+++ openssh-3.0p1J/channels.h	Tue Nov 13 15:55:52 2001
@@ -218,6 +218,7 @@
 void	 chan_mark_dead(Channel *);
 void 	 chan_init_iostates(Channel *);
 void	 chan_init(void);
+void	 chan_shutdown_read(Channel *);
 
 typedef void    chan_event_fn(Channel *);
 
diff -ur openssh-3.0p1/clientloop.c openssh-3.0p1J/clientloop.c
--- openssh-3.0p1/clientloop.c	Thu Oct 11 19:36:09 2001
+++ openssh-3.0p1J/clientloop.c	Tue Nov 13 16:06:36 2001
@@ -84,6 +84,7 @@
 
 /* import options */
 extern Options options;
+extern int no_tty_flag;
 
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
@@ -122,7 +123,8 @@
 static int connection_in;	/* Connection to server (input). */
 static int connection_out;	/* Connection to server (output). */
 static int need_rekeying;	/* Set to non-zero if rekeying is requested. */
-static int session_closed = 0;	/* In SSH2: login session closed. */
+enum SessionStatus {SessionOpen, SessionClose, SessionWait};
+static int session_status = SessionOpen; /* In SSH2: login session closed. */
 
 static void client_init_dispatch(void);
 int	session_ident = -1;
@@ -320,6 +322,10 @@
 client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
     int *maxfdp, int *nallocp, int rekeying)
 {
+	struct timeval timer;
+	struct timeval *timerp;
+	int rc;
+	
 	/* Add any selections by the channel mechanism. */
 	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
 
@@ -343,7 +349,7 @@
 			FD_SET(fileno(stderr), *writesetp);
 	} else {
 		/* channel_prepare_select could have closed the last channel */
-		if (session_closed && !channel_still_open() &&
+		if (session_status == SessionClose && !channel_still_open() &&
 		    !packet_have_data_to_write()) {
 			/* clear mask since we did not call select() */
 			memset(*readsetp, 0, *maxfdp);
@@ -367,7 +373,17 @@
 	 * SSH_MSG_IGNORE packet when the timeout expires.
 	 */
 
-	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
+	if((session_status == SessionWait && options.sleep > 0) ||
+	    (no_tty_flag && options.sleep == -1)) {
+		timer.tv_sec=options.sleep > 0 ? options.sleep : 0;
+		timer.tv_usec=0;
+		timerp=&timer;
+	} else {
+		timerp=NULL;
+	}
+	
+	rc=select((*maxfdp)+1, *readsetp, *writesetp, NULL, timerp);
+	if (rc < 0) {
 		char buf[100];
 
 		/*
@@ -384,7 +400,8 @@
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
-	}
+	} else if (rc == 0 && session_status == SessionWait)
+		session_status=SessionClose;
 }
 
 static void
@@ -445,9 +462,12 @@
 		len = read(connection_in, buf, sizeof(buf));
 		if (len == 0) {
 			/* Received EOF.  The remote host has closed the connection. */
+/* This message duplicates the one already in client_loop(). */
+#if 0 
 			snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n",
 				 host);
 			buffer_append(&stderr_buffer, buf, strlen(buf));
+#endif
 			quit_pending = 1;
 			return;
 		}
@@ -757,7 +777,7 @@
 		error("client_channel_closed: id %d != session_ident %d",
 		    id, session_ident);
 	channel_cancel_cleanup(id);
-	session_closed = 1;
+ 	session_status = (options.sleep >= 0) ? SessionWait : SessionClose;
 	if (in_raw_mode())
 		leave_raw_mode();
 }
@@ -782,6 +802,7 @@
 	start_time = get_current_time();
 
 	/* Initialize variables. */
+	if(!have_pty) session_status=SessionWait;
 	escape_pending = 0;
 	last_was_cr = 1;
 	exit_status = -1;
@@ -846,7 +867,8 @@
 		/* Process buffered packets sent by the server. */
 		client_process_buffered_input_packets();
 
-		if (compat20 && session_closed && !channel_still_open())
+		if (compat20 && (session_status == SessionClose)
+		    && !channel_still_open())
 			break;
 
 		rekeying = (xxx_kex != NULL && !xxx_kex->done);
diff -ur openssh-3.0p1/nchan.c openssh-3.0p1J/nchan.c
--- openssh-3.0p1/nchan.c	Thu Oct 11 19:35:06 2001
+++ openssh-3.0p1J/nchan.c	Tue Nov 13 15:55:52 2001
@@ -81,7 +81,7 @@
 
 /* helper */
 static void	chan_shutdown_write(Channel *);
-static void	chan_shutdown_read(Channel *);
+void		chan_shutdown_read(Channel *);
 
 /*
  * SSH1 specific implementation of event functions
@@ -533,7 +533,7 @@
 			    c->self, c->wfd, strerror(errno));
 	}
 }
-static void
+void
 chan_shutdown_read(Channel *c)
 {
 	if (compat20 && c->type == SSH_CHANNEL_LARVAL)
diff -ur openssh-3.0p1/readconf.c openssh-3.0p1J/readconf.c
--- openssh-3.0p1/readconf.c	Wed Oct  3 11:39:39 2001
+++ openssh-3.0p1J/readconf.c	Tue Nov 13 16:09:00 2001
@@ -115,7 +115,7 @@
 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
 	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
-	oClearAllForwardings, oNoHostAuthenticationForLocalhost 
+	oClearAllForwardings, oNoHostAuthenticationForLocalhost, oSleep 
 } OpCodes;
 
 /* Textual representations of the tokens. */
@@ -187,6 +187,7 @@
 	{ "smartcarddevice", oSmartcardDevice },
 	{ "clearallforwardings", oClearAllForwardings }, 
 	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, 
+  	{ "sleep", oSleep },
 	{ NULL, 0 }
 };
 
@@ -528,6 +529,10 @@
 		intptr = &options->connection_attempts;
 		goto parse_int;
 
+	case oSleep:
+		intptr = &options->sleep;
+		goto parse_int;
+
 	case oCipher:
 		intptr = &options->cipher;
 		arg = strdelim(&s);
@@ -799,6 +804,7 @@
 	options->bind_address = NULL;
 	options->smartcard_device = NULL;
 	options->no_host_authentication_for_localhost = - 1;
+	options->sleep = -1;
 }
 
 /*
diff -ur openssh-3.0p1/readconf.h openssh-3.0p1J/readconf.h
--- openssh-3.0p1/readconf.h	Wed Oct  3 11:39:39 2001
+++ openssh-3.0p1J/readconf.h	Tue Nov 13 16:10:26 2001
@@ -102,6 +102,7 @@
 	Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION];
 	int	clear_forwardings;
 	int	no_host_authentication_for_localhost;
+        int     sleep;		/* Exit delay in seconds */
 }       Options;
 
 
diff -ur openssh-3.0p1/session.c openssh-3.0p1J/session.c
--- openssh-3.0p1/session.c	Sun Oct 28 04:34:53 2001
+++ openssh-3.0p1J/session.c	Tue Nov 13 15:55:52 2001
@@ -1919,6 +1919,9 @@
 	 */
 	if (c->ostate != CHAN_OUTPUT_CLOSED)
 		chan_write_failed(c);
+	if (c->istate == CHAN_INPUT_OPEN && compat20) {
+		chan_shutdown_read(c);
+	}
 	s->chanid = -1;
 }
 
diff -ur openssh-3.0p1/ssh.c openssh-3.0p1J/ssh.c
--- openssh-3.0p1/ssh.c	Tue Oct  9 23:07:45 2001
+++ openssh-3.0p1J/ssh.c	Tue Nov 13 15:55:52 2001
@@ -195,6 +195,7 @@
 	fprintf(stderr, "              These cause %s to listen for connections on a port, and\n", __progname);
 	fprintf(stderr, "              forward them to the other side by connecting to host:port.\n");
 	fprintf(stderr, "  -D port     Enable dynamic application-level port forwarding.\n");
+ 	fprintf(stderr, "  -S delay    Set exit delay (in seconds; 0 means wait forever).\n");
 	fprintf(stderr, "  -C          Enable compression.\n");
 	fprintf(stderr, "  -N          Do not execute a shell or command.\n");
 	fprintf(stderr, "  -g          Allow remote hosts to connect to forwarded ports.\n");
@@ -320,7 +321,7 @@
 
 again:
 	while ((opt = getopt(ac, av,
-	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVX")) != -1) {
+	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:S:TVX")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -495,7 +496,13 @@
 				add_remote_forward(&options, fwd_port, buf,
 				     fwd_host_port);
 			break;
-
+		case 'S':
+			options.sleep = atoi(optarg);
+			if (options.sleep < 0) {
+				fprintf(stderr, "Bad delay value '%s'\n", optarg);
+				exit(1);
+			}
+			break;
 		case 'D':
 			fwd_port = a2port(optarg);
 			if (fwd_port == 0) {
diff -ur openssh-3.0p1/version.h openssh-3.0p1J/version.h
--- openssh-3.0p1/version.h	Wed Oct 24 09:20:57 2001
+++ openssh-3.0p1J/version.h	Tue Nov 13 16:11:16 2001
@@ -1,3 +1,3 @@
 /* $OpenBSD: version.h,v 1.25 2001/10/15 16:10:50 deraadt Exp $ */
 
-#define SSH_VERSION	"OpenSSH_3.0p1"
+#define SSH_VERSION	"OpenSSH_3.0p1J"



More information about the openssh-unix-dev mailing list