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:
42
src/exec.c
42
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])
|
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. */
|
||||||
|
Reference in New Issue
Block a user