]> granicus.if.org Git - sudo/commitdiff
When not logging I/O, put command in its own pgrp and make that the
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 3 Jan 2012 15:06:07 +0000 (10:06 -0500)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 3 Jan 2012 15:06:07 +0000 (10:06 -0500)
controlling pgrp if the command is in the foreground.  Fixes a race
in the non-I/O logging path where the command may receive two
keyboard-generated signals; one from the kernel and one from the
sudo process.

src/exec.c

index 49570751a9559135b0186562944c4d94be37ec7a..ee2d08a6a8454493518705c420264094119512dd 100644 (file)
@@ -109,6 +109,7 @@ my_execve(const char *path, char *const argv[], char *const envp[])
  */
 static int fork_cmnd(struct command_details *details, int sv[2])
 {
+    int ttyfd, foreground = 0;
     struct command_status cstat;
     sigaction_t sa;
     pid_t child;
@@ -120,6 +121,12 @@ static int fork_cmnd(struct command_details *details, int sv[2])
     sa.sa_handler = handler;
     sigaction(SIGCONT, &sa, NULL);
 
+    ttyfd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
+    if (ttyfd != -1) {
+       foreground = (tcgetpgrp(ttyfd) == getpgrp());
+       (void)fcntl(ttyfd, F_SETFD, FD_CLOEXEC);
+    }
+
     child = fork();
     switch (child) {
     case -1:
@@ -133,6 +140,16 @@ static int fork_cmnd(struct command_details *details, int sv[2])
        fcntl(sv[1], F_SETFD, FD_CLOEXEC);
        restore_signals();
        if (exec_setup(details, NULL, -1) == true) {
+           /* Set child process group here too to avoid a race. */
+           child = getpid();
+           setpgid(0, child);
+
+           /* Wait for parent to grant us the tty if we are foreground. */
+           if (foreground) {
+               while (tcgetpgrp(ttyfd) != child)
+                   ; /* spin */
+           }
+
            /* headed for execve() */
            sudo_debug_execve(SUDO_DEBUG_INFO, details->command,
                details->argv, details->envp);
@@ -160,6 +177,19 @@ static int fork_cmnd(struct command_details *details, int sv[2])
        sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 1);
        _exit(1);
     }
+
+    /*
+     * Put child in its own process group.  If we are starting the command
+     * in the foreground, assign its pgrp to the tty.
+     */
+    setpgid(child, child);
+    if (foreground) {
+       while (tcsetpgrp(ttyfd, child) == -1 && errno == EINTR)
+           continue;
+    }
+    if (ttyfd != -1)
+       close(ttyfd);
+
     debug_return_int(child);
 }
 
@@ -504,17 +534,17 @@ handle_signals(int fd, pid_t child, int log_io, struct command_status *cstat)
                         * so we can restore it after we resume.
                         */
                        pid_t saved_pgrp = (pid_t)-1;
-                       int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
-                       if (fd != -1)
-                           saved_pgrp = tcgetpgrp(fd);
+                       int ttyfd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
+                       if (ttyfd != -1)
+                           saved_pgrp = tcgetpgrp(ttyfd);
                        if (kill(getpid(), WSTOPSIG(status)) != 0) {
                            warning("kill(%d, %d)", (int)getpid(),
                                WSTOPSIG(status));
                        }
-                       if (fd != -1) {
+                       if (ttyfd != -1) {
                            if (saved_pgrp != (pid_t)-1)
-                               (void)tcsetpgrp(fd, saved_pgrp);
-                           close(fd);
+                               (void)tcsetpgrp(ttyfd, saved_pgrp);
+                           close(ttyfd);
                        }
                    } else {
                        /* Child has exited, we are done. */