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])
{
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. */