]> granicus.if.org Git - sudo/commitdiff
Fix a race between when we get the child pid in the parent and when
authorTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 8 Apr 2010 10:12:47 +0000 (06:12 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 8 Apr 2010 10:12:47 +0000 (06:12 -0400)
the child process exits.  The problem exhibited as a hang after a
short-lived process, e.g. "sudo id" when no IO logger was enabled.

src/script.c

index e98970b34711a55d2c9108418dd4855a5051c5db..2bc8ecf8b2ca1c3e488b39231d6058f18f713d12 100644 (file)
@@ -111,7 +111,6 @@ static void flush_output(struct script_buf *output);
 static void handler(int s);
 static int script_child(const char *path, char *argv[], char *envp[], int, int);
 static void script_run(const char *path, char *argv[], char *envp[], int);
-static void sigchild(int s);
 static void sigwinch(int s);
 static void sync_ttysize(int src, int dst);
 static void deliver_signal(pid_t pid, int signo);
@@ -349,7 +348,7 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
 
     /* Note: HP-UX select() will not be interrupted if SA_RESTART set */
     sa.sa_flags = 0;
-    sa.sa_handler = sigchild;
+    sa.sa_handler = handler;
     sigaction(SIGCHLD, &sa, NULL);
 
     if (log_io) {
@@ -473,10 +472,25 @@ script_execve(struct command_details *details, char *argv[], char *envp[],
     zero_bytes(&input, sizeof(input));
     zero_bytes(&output, sizeof(output));
     for (;;) {
-       /* XXX - racey */
-       if (!relaysig && recvsig != SIGCHLD) {
-           relaysig = recvsig;
+       /* Wait for children as needed. */
+       if (recvsig == SIGCHLD) {
+           pid_t pid;
+           int flags = WNOHANG;
+
            recvsig = 0;
+           if (log_io)
+               flags |= WUNTRACED;
+           do {
+               do {
+                   pid = waitpid(child, &child_status, flags);
+               } while (pid == -1 && errno == EINTR);
+               if (pid == child) {
+                   if (!log_io)
+                       recvsig = SIGCHLD; /* XXX - hacky, see below */
+                   else if (WIFSTOPPED(child_status))
+                       relaysig = WSTOPSIG(child_status);
+               }
+           } while (pid > 0);
        }
 
        /* If not logging I/O and child has exited we are done. */
@@ -942,33 +956,6 @@ sync_ttysize(int src, int dst)
 #endif
 }
 
-/*
- * Handler for SIGCHLD in parent
- * Note that this will detect when the child monitoring the command exits,
- * not the command itself.
- */
-static void
-sigchild(int s)
-{
-    pid_t pid;
-    int flags = WNOHANG;
-    int serrno = errno;
-
-    if (!tq_empty(&io_plugins))
-       flags |= WUNTRACED;
-    do {
-       pid = waitpid(child, &child_status, flags);
-    } while (pid == -1 && errno == EINTR);
-    if (pid == child) {
-       if (tq_empty(&io_plugins))
-           recvsig = SIGCHLD;
-       else if (WIFSTOPPED(child_status))
-           recvsig = WSTOPSIG(child_status);
-    }
-
-    errno = serrno;
-}
-
 /*
  * Generic handler for signals passed from parent -> child
  */