From 5bc0756406c6bd666105fb3d0e7782a6891c9ac9 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Tue, 3 Jan 2012 10:06:07 -0500 Subject: [PATCH] When not logging I/O, put command in its own pgrp and make that the 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 | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/exec.c b/src/exec.c index 49570751a..ee2d08a6a 100644 --- a/src/exec.c +++ b/src/exec.c @@ -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. */ -- 2.40.0