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.
This commit is contained in:
Todd C. Miller
2012-01-03 10:06:07 -05:00
parent 7799d01cfa
commit 5bc0756406

View 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]) static int fork_cmnd(struct command_details *details, int sv[2])
{ {
int ttyfd, foreground = 0;
struct command_status cstat; struct command_status cstat;
sigaction_t sa; sigaction_t sa;
pid_t child; pid_t child;
@@ -120,6 +121,12 @@ static int fork_cmnd(struct command_details *details, int sv[2])
sa.sa_handler = handler; sa.sa_handler = handler;
sigaction(SIGCONT, &sa, NULL); 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(); child = fork();
switch (child) { switch (child) {
case -1: case -1:
@@ -133,6 +140,16 @@ static int fork_cmnd(struct command_details *details, int sv[2])
fcntl(sv[1], F_SETFD, FD_CLOEXEC); fcntl(sv[1], F_SETFD, FD_CLOEXEC);
restore_signals(); restore_signals();
if (exec_setup(details, NULL, -1) == true) { 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() */ /* headed for execve() */
sudo_debug_execve(SUDO_DEBUG_INFO, details->command, sudo_debug_execve(SUDO_DEBUG_INFO, details->command,
details->argv, details->envp); 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); sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 1);
_exit(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); 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. * so we can restore it after we resume.
*/ */
pid_t saved_pgrp = (pid_t)-1; pid_t saved_pgrp = (pid_t)-1;
int fd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0); int ttyfd = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
if (fd != -1) if (ttyfd != -1)
saved_pgrp = tcgetpgrp(fd); saved_pgrp = tcgetpgrp(ttyfd);
if (kill(getpid(), WSTOPSIG(status)) != 0) { if (kill(getpid(), WSTOPSIG(status)) != 0) {
warning("kill(%d, %d)", (int)getpid(), warning("kill(%d, %d)", (int)getpid(),
WSTOPSIG(status)); WSTOPSIG(status));
} }
if (fd != -1) { if (ttyfd != -1) {
if (saved_pgrp != (pid_t)-1) if (saved_pgrp != (pid_t)-1)
(void)tcsetpgrp(fd, saved_pgrp); (void)tcsetpgrp(ttyfd, saved_pgrp);
close(fd); close(ttyfd);
} }
} else { } else {
/* Child has exited, we are done. */ /* Child has exited, we are done. */