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