When not logging I/O, use a signal handler that only forwards SIGINT,
SIGQUIT and SIGHUP when they are user-generated signals. 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:
47
src/exec.c
47
src/exec.c
@@ -78,6 +78,9 @@ static int handle_signals(int fd, pid_t child, int log_io,
|
|||||||
struct command_status *cstat);
|
struct command_status *cstat);
|
||||||
static void forward_signals(int fd);
|
static void forward_signals(int fd);
|
||||||
static void schedule_signal(int signo);
|
static void schedule_signal(int signo);
|
||||||
|
#ifdef SA_SIGINFO
|
||||||
|
static void handler_nofwd(int s, siginfo_t *info, void *context);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Like execve(2) but falls back to running through /bin/sh
|
* Like execve(2) but falls back to running through /bin/sh
|
||||||
@@ -286,14 +289,28 @@ sudo_execve(struct command_details *details, struct command_status *cstat)
|
|||||||
sa.sa_handler = handler;
|
sa.sa_handler = handler;
|
||||||
sigaction(SIGALRM, &sa, NULL);
|
sigaction(SIGALRM, &sa, NULL);
|
||||||
sigaction(SIGCHLD, &sa, NULL);
|
sigaction(SIGCHLD, &sa, NULL);
|
||||||
sigaction(SIGHUP, &sa, NULL);
|
|
||||||
sigaction(SIGINT, &sa, NULL);
|
|
||||||
sigaction(SIGPIPE, &sa, NULL);
|
sigaction(SIGPIPE, &sa, NULL);
|
||||||
sigaction(SIGQUIT, &sa, NULL);
|
|
||||||
sigaction(SIGTERM, &sa, NULL);
|
sigaction(SIGTERM, &sa, NULL);
|
||||||
sigaction(SIGUSR1, &sa, NULL);
|
sigaction(SIGUSR1, &sa, NULL);
|
||||||
sigaction(SIGUSR2, &sa, NULL);
|
sigaction(SIGUSR2, &sa, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When not running the command in a pty, we do not want to
|
||||||
|
* forward signals generated by the kernel that the child will
|
||||||
|
* already have received either by virtue of being in the
|
||||||
|
* controlling tty's process group (SIGINT, SIGQUIT) or because
|
||||||
|
* the session is terminating (SIGHUP).
|
||||||
|
*/
|
||||||
|
#ifdef SA_SIGINFO
|
||||||
|
if (!log_io) {
|
||||||
|
sa.sa_flags |= SA_SIGINFO;
|
||||||
|
sa.sa_sigaction = handler_nofwd;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
sigaction(SIGHUP, &sa, NULL);
|
||||||
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
sigaction(SIGQUIT, &sa, NULL);
|
||||||
|
|
||||||
/* Max fd we will be selecting on. */
|
/* Max fd we will be selecting on. */
|
||||||
maxfd = MAX(sv[0], signal_pipe[0]);
|
maxfd = MAX(sv[0], signal_pipe[0]);
|
||||||
|
|
||||||
@@ -617,6 +634,30 @@ handler(int s)
|
|||||||
/* shut up glibc */;
|
/* shut up glibc */;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SA_SIGINFO
|
||||||
|
/*
|
||||||
|
* Generic handler for signals passed from parent -> child.
|
||||||
|
* The other end of signal_pipe is checked in the main event loop.
|
||||||
|
* This version is for the non-pty case and does not forward
|
||||||
|
* signals that are generated by the kernel.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handler_nofwd(int s, siginfo_t *info, void *context)
|
||||||
|
{
|
||||||
|
unsigned char signo = (unsigned char)s;
|
||||||
|
|
||||||
|
/* Only forward user-generated signals. */
|
||||||
|
if (info->si_code <= 0) {
|
||||||
|
/*
|
||||||
|
* The pipe is non-blocking, if we overflow the kernel's pipe
|
||||||
|
* buffer we drop the signal. This is not a problem in practice.
|
||||||
|
*/
|
||||||
|
if (write(signal_pipe[1], &signo, sizeof(signo)) == -1)
|
||||||
|
/* shut up glibc */;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* SA_SIGINFO */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open a pipe and make both ends non-blocking.
|
* Open a pipe and make both ends non-blocking.
|
||||||
* Returns 0 on success and -1 on error.
|
* Returns 0 on success and -1 on error.
|
||||||
|
Reference in New Issue
Block a user