suggested fix for the sigchld race

Markus Friedl markus at openbsd.org
Thu Nov 1 03:42:01 EST 2001


comments?

alternatives: sigsetjmp(ugly) and pselect(not portable, available)
drawback: additional filedescriptors.

Index: serverloop.c
===================================================================
RCS file: /home/markus/cvs/ssh/serverloop.c,v
retrieving revision 1.82
diff -u -r1.82 serverloop.c
--- serverloop.c	10 Oct 2001 22:18:47 -0000	1.82
+++ serverloop.c	11 Oct 2001 18:06:33 -0000
@@ -92,6 +92,45 @@
 /* prototypes */
 static void server_init_dispatch(void);
 
+/*
+ * we write to this pipe if a SIGCHLD is caught in order to avoid
+ * the race between select() and child_terminated
+ */
+static int notify_pipe[2];
+static void
+notify_setup(void)
+{
+	if (pipe(notify_pipe) < 0) {
+		error("pipe(notify_pipe) failed %s", strerror(errno));
+		notify_pipe[0] = -1;	/* read end */
+		notify_pipe[1] = -1;	/* write end */
+	} else {
+		set_nonblock(notify_pipe[0]);
+		set_nonblock(notify_pipe[1]);
+	}
+}
+static void
+notify_parent(void)
+{
+	if (notify_pipe[1] != -1)
+		write(notify_pipe[1], "", 1);
+}
+static void
+notify_prepare(fd_set *readset)
+{
+	if (notify_pipe[0] != -1)
+		FD_SET(notify_pipe[0], readset);
+}
+static void
+notify_done(fd_set *readset)
+{
+	char c;
+
+	if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset))
+		while (read(notify_pipe[0], &c, 1) != -1)
+			debug2("notify_done: reading");
+}
+
 static void
 sigchld_handler(int sig)
 {
@@ -99,6 +138,7 @@
 	debug("Received SIGCHLD.");
 	child_terminated = 1;
 	signal(SIGCHLD, sigchld_handler);
+	notify_parent();
 	errno = save_errno;
 }
 
@@ -242,6 +282,7 @@
 		if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
 			FD_SET(fdin, *writesetp);
 	}
+	notify_prepare(*readsetp);
 
 	/*
 	 * If we have buffered packet data going to the client, mark that
@@ -278,6 +319,8 @@
 			error("select: %.100s", strerror(errno));
 	} else if (ret == 0 && client_alive_scheduled)
 		client_alive_check();
+
+	notify_done(*readsetp);
 }
 
 /*
@@ -467,6 +510,8 @@
 	connection_in = packet_get_connection_in();
 	connection_out = packet_get_connection_out();
 
+	notify_setup();
+
 	previous_stdout_buffer_bytes = 0;
 
 	/* Set approximate I/O buffer size. */
@@ -572,6 +617,7 @@
 		max_fd = MAX(max_fd, fdin);
 		max_fd = MAX(max_fd, fdout);
 		max_fd = MAX(max_fd, fderr);
+		max_fd = MAX(max_fd, notify_pipe[0]);
 
 		/* Sleep in select() until we can do something. */
 		wait_until_can_do_something(&readset, &writeset, &max_fd,
@@ -696,7 +742,11 @@
 	connection_in = packet_get_connection_in();
 	connection_out = packet_get_connection_out();
 
+	notify_setup();
+
 	max_fd = MAX(connection_in, connection_out);
+	max_fd = MAX(max_fd, notify_pipe[0]);
+
 	xxx_authctxt = authctxt;
 
 	server_init_dispatch();



More information about the openssh-unix-dev mailing list