Suspend the child process and wait for SIGUSR when using ptrace.

This fixes a race condition in ptrace-based intercept mode when
running the command in a pty.  It was possible for the monitor to
receive SIGCHLD when the command sent itself SIGSTOP before the
main sudo process did.
This commit is contained in:
Todd C. Miller
2022-04-29 13:09:03 -06:00
parent fe80dc0bc2
commit 423fbedb65
4 changed files with 78 additions and 44 deletions

View File

@@ -344,6 +344,14 @@ free_exec_closure_nopty(struct exec_closure_nopty *ec)
debug_return;
}
#ifdef HAVE_PTRACE_INTERCEPT
static void
handler(int signo)
{
/* just return */
}
#endif /* HAVE_PTRACE_INTERCEPT */
/*
* Execute a command and wait for it to finish.
*/
@@ -414,10 +422,29 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
break;
case 0:
/* child */
sigprocmask(SIG_SETMASK, &oset, NULL);
close(errpipe[0]);
if (intercept_sv[0] != -1)
close(intercept_sv[0]);
#ifdef HAVE_PTRACE_INTERCEPT
if (ISSET(details->flags, CD_USE_PTRACE)) {
struct sigaction sa;
/* Tracer will send us SIGUSR1 when it is time to proceed. */
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sa.sa_handler = handler;
if (sudo_sigaction(SIGUSR1, &sa, NULL) != 0) {
sudo_warn(U_("unable to set handler for signal %d"),
SIGUSR1);
}
/* Suspend child until tracer seizes control and sends SIGUSR1. */
sigdelset(&set, SIGUSR1);
sigsuspend(&set);
}
#endif /* HAVE_PTRACE_INTERCEPT */
sigprocmask(SIG_SETMASK, &oset, NULL);
exec_cmnd(details, intercept_sv[1], errpipe[1]);
while (write(errpipe[1], &errno, sizeof(int)) == -1) {
if (errno != EINTR)