diff --git a/src/exec.c b/src/exec.c index 5f87530131..5faa77438a 100644 --- a/src/exec.c +++ b/src/exec.c @@ -245,9 +245,12 @@ exec_setup(struct command_details *details, int intercept_fd, int errfd) * If SELinux is enabled, run the command via sesh, otherwise * execute it directly. * If the exec fails, cstat is filled in with the value of errno. + * If mask is not NULL, it is the signal mask to restore after the + * signal handlers have been reset. */ void -exec_cmnd(struct command_details *details, int intercept_fd, int errfd) +exec_cmnd(struct command_details *details, sigset_t *mask, int intercept_fd, + int errfd) { debug_decl(exec_cmnd, SUDO_DEBUG_EXEC); @@ -261,7 +264,13 @@ exec_cmnd(struct command_details *details, int intercept_fd, int errfd) } #endif /* HAVE_PTRACE_INTERCEPT */ + /* + * Reset the signal handlers before unblocking signals so that a + * signal forwarded by the parent before execve(2) is not lost. + */ restore_signals(); + if (mask != NULL) + sigprocmask(SIG_SETMASK, mask, NULL); if (exec_setup(details, intercept_fd, errfd) == true) { /* headed for execve() */ #ifdef HAVE_SELINUX @@ -473,7 +482,7 @@ sudo_execute(struct command_details *details, */ if (direct_exec_allowed(details)) { if (!sudo_terminated(cstat)) { - exec_cmnd(details, -1, -1); + exec_cmnd(details, NULL, -1, -1); cstat->type = CMD_ERRNO; cstat->val = errno; } diff --git a/src/exec_monitor.c b/src/exec_monitor.c index 9546be62ab..34f6f62afa 100644 --- a/src/exec_monitor.c +++ b/src/exec_monitor.c @@ -346,7 +346,7 @@ mon_backchannel_cb(int fd, int what, void *v) */ static void exec_cmnd_pty(struct command_details *details, bool foreground, - int intercept_fd, int errfd) + sigset_t *mask, int intercept_fd, int errfd) { volatile pid_t self = getpid(); debug_decl(exec_cmnd_pty, SUDO_DEBUG_EXEC); @@ -396,7 +396,7 @@ exec_cmnd_pty(struct command_details *details, bool foreground, /* Execute command; only returns on error. */ sudo_debug_printf(SUDO_DEBUG_INFO, "executing %s in the %s", details->command, foreground ? "foreground" : "background"); - exec_cmnd(details, intercept_fd, errfd); + exec_cmnd(details, mask, intercept_fd, errfd); debug_return; } @@ -551,6 +551,7 @@ exec_monitor(struct command_details *details, bool foreground, struct monitor_closure mc; struct command_status cstat; struct sigaction sa; + sigset_t set, oset; int errsock[2]; debug_decl(exec_monitor, SUDO_DEBUG_EXEC); @@ -621,10 +622,19 @@ exec_monitor(struct command_details *details, bool foreground, */ init_exec_events_monitor(&mc, errsock[0]); + /* + * Block signals across the fork; the child unblocks them once it + * has reset the handlers (see exec_cmnd()) so that a signal + * delivered to it before execve(2) is not lost. + */ + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, &oset); + mc.cmnd_pid = sudo_debug_fork(); switch (mc.cmnd_pid) { case -1: sudo_warn("%s", U_("unable to fork")); + sigprocmask(SIG_SETMASK, &oset, NULL); #ifdef HAVE_SELINUX if (ISSET(details->flags, CD_RBAC_ENABLED)) { if (selinux_restore_tty() != 0) @@ -637,12 +647,13 @@ exec_monitor(struct command_details *details, bool foreground, close(backchannel); close(errsock[0]); /* setup tty and exec command */ - exec_cmnd_pty(details, foreground, intercept_fd, errsock[1]); + exec_cmnd_pty(details, foreground, &oset, intercept_fd, errsock[1]); if (send(errsock[1], &errno, sizeof(int), 0) == -1) sudo_warn(U_("unable to execute %s"), details->command); _exit(EXIT_FAILURE); /* NOTREACHED */ } + sigprocmask(SIG_SETMASK, &oset, NULL); close(errsock[1]); if (intercept_fd != -1) close(intercept_fd); diff --git a/src/exec_nopty.c b/src/exec_nopty.c index dde59699be..dde2854427 100644 --- a/src/exec_nopty.c +++ b/src/exec_nopty.c @@ -615,9 +615,11 @@ exec_nopty(struct command_details *details, /* Allocate and set signal events and the error pipe event.*/ init_exec_events(&ec, evbase, errpipe[0]); - /* Restore signal mask now that signal handlers are setup. */ - sigprocmask(SIG_SETMASK, &oset, NULL); - + /* + * Signals stay blocked across the fork; the child unblocks them + * once it has reset the handlers (see exec_cmnd()) so that a + * signal forwarded to it before execve(2) is not lost. + */ ec.cmnd_pid = sudo_debug_fork(); switch (ec.cmnd_pid) { case -1: @@ -647,7 +649,7 @@ exec_nopty(struct command_details *details, close(io_pipe[STDERR_FILENO][0]); close(io_pipe[STDERR_FILENO][1]); } - exec_cmnd(details, intercept_sv[1], errpipe[1]); + exec_cmnd(details, &oset, intercept_sv[1], errpipe[1]); while (write(errpipe[1], &errno, sizeof(int)) == -1) { if (errno != EINTR) break; @@ -658,6 +660,9 @@ exec_nopty(struct command_details *details, } sudo_debug_printf(SUDO_DEBUG_INFO, "executed %s, pid %d", details->command, (int)ec.cmnd_pid); + + /* Restore signal mask now that the command has been forked. */ + sigprocmask(SIG_SETMASK, &oset, NULL); /* Close the other end of the pipes and socketpairs. */ if (io_pipe[STDIN_FILENO][0] != -1) close(io_pipe[STDIN_FILENO][0]); diff --git a/src/signal.c b/src/signal.c index 18e9ddc323..633aa6c9b6 100644 --- a/src/signal.c +++ b/src/signal.c @@ -86,16 +86,20 @@ restore_signals(void) struct signal_state *ss; debug_decl(restore_signals, SUDO_DEBUG_MAIN); + /* + * Restore all saved signals, not just the ones flagged for restore. + * The handlers sudo installed are inherited across fork(2) and a + * signal that arrives before execve(2) would be swallowed by them. + * The saved state is what execve(2) would leave: SIG_IGN or SIG_DFL. + */ for (ss = saved_signals; ss->signo != -1; ss++) { - if (ss->restore) { - sudo_debug_printf(SUDO_DEBUG_INFO, - "restoring handler for signal %d: %s", ss->signo, - ss->sa.sa_handler == SIG_IGN ? "SIG_IGN" : - ss->sa.sa_handler == SIG_DFL ? "SIG_DFL" : "???"); - if (sigaction(ss->signo, &ss->sa, NULL) != 0) { - sudo_warn(U_("unable to restore handler for signal %d"), - ss->signo); - } + sudo_debug_printf(SUDO_DEBUG_INFO, + "restoring handler for signal %d: %s", ss->signo, + ss->sa.sa_handler == SIG_IGN ? "SIG_IGN" : + ss->sa.sa_handler == SIG_DFL ? "SIG_DFL" : "???"); + if (sigaction(ss->signo, &ss->sa, NULL) != 0) { + sudo_warn(U_("unable to restore handler for signal %d"), + ss->signo); } } diff --git a/src/sudo_exec.h b/src/sudo_exec.h index 7db0d5bb9d..2e673f74fe 100644 --- a/src/sudo_exec.h +++ b/src/sudo_exec.h @@ -176,7 +176,8 @@ union sudo_token_un { /* exec.c */ struct stat; -void exec_cmnd(struct command_details *details, int intercept_fd, int errfd); +void exec_cmnd(struct command_details *details, sigset_t *mask, + int intercept_fd, int errfd); void terminate_command(pid_t pid, bool use_pgrp); bool sudo_terminated(struct command_status *cstat); void free_exec_closure(struct exec_closure *ec);