Fix suspending a sudo-run shell in ptrace intercept mode with no pty.
When ptracing a process, we receive the signal-delivery-stop signal before the group-stop signal. If sudo is running the command in the same terminal, we need to wait until the stop signal is actually delivered to the command before we can suspend sudo itself. If we suspend sudo before receiving the group-stop, the command will be restarted with PTRACE_LISTEN too late and will miss the SIGCONT from sudo.
This commit is contained in:
@@ -568,8 +568,8 @@ handle_sigchld_nopty(struct exec_closure_nopty *ec)
|
||||
"%s: process %d stopped, SIG%s", __func__, (int)pid, signame);
|
||||
|
||||
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
|
||||
/* Did exec_ptrace_handled() suppress the signal? */
|
||||
if (exec_ptrace_handled(pid, status, ec->intercept))
|
||||
/* If not a group-stop signal, just continue. */
|
||||
if (!exec_ptrace_stopped(pid, status, ec->intercept))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -1195,17 +1195,19 @@ done:
|
||||
|
||||
/*
|
||||
* Handle a process stopped due to ptrace.
|
||||
* Returns true if the signal was suppressed and false if it was delivered.
|
||||
* Restarts the tracee with PTRACE_LISTEN (for a group-stop)
|
||||
* or PTRACE_CONT (for signal-delivery-stop).
|
||||
* Returns true if stopped by a group-stop, else false.
|
||||
*/
|
||||
bool
|
||||
exec_ptrace_handled(pid_t pid, int status, void *intercept)
|
||||
exec_ptrace_stopped(pid_t pid, int status, void *intercept)
|
||||
{
|
||||
struct intercept_closure *closure = intercept;
|
||||
const int stopsig = WSTOPSIG(status);
|
||||
const int sigtrap = status >> 8;
|
||||
long signo = 0;
|
||||
bool group_stop = false;
|
||||
debug_decl(exec_ptrace_handled, SUDO_DEBUG_EXEC);
|
||||
debug_decl(exec_ptrace_stopped, SUDO_DEBUG_EXEC);
|
||||
|
||||
if (sigtrap == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) {
|
||||
if (!ptrace_intercept_execve(pid, closure)) {
|
||||
@@ -1255,24 +1257,22 @@ exec_ptrace_handled(pid_t pid, int status, void *intercept)
|
||||
}
|
||||
}
|
||||
|
||||
/* Continue child. */
|
||||
/* XXX - handle ptrace returning ESRCH if process dies */
|
||||
if (group_stop) {
|
||||
/*
|
||||
* Restart child but prevent it from executing
|
||||
* until SIGCONT is received (simulate SIGSTOP, etc).
|
||||
*/
|
||||
if (ptrace(PTRACE_LISTEN, pid, NULL, 0L) == -1)
|
||||
sudo_warn("%s: ptrace(PTRACE_LISTEN, %d, NULL, %d)",
|
||||
__func__, (int)pid, stopsig);
|
||||
if (ptrace(PTRACE_LISTEN, pid, NULL, 0L) == -1 && errno != ESRCH)
|
||||
sudo_warn("%s: ptrace(PTRACE_LISTEN, %d, NULL, 0L)",
|
||||
__func__, (int)pid);
|
||||
} else {
|
||||
/* Restart child. */
|
||||
if (ptrace(PTRACE_CONT, pid, NULL, signo) == -1)
|
||||
/* Restart child immediately. */
|
||||
if (ptrace(PTRACE_CONT, pid, NULL, signo) == -1 && errno != ESRCH)
|
||||
sudo_warn("%s: ptrace(PTRACE_CONT, %d, NULL, %ld)",
|
||||
__func__, (int)pid, signo);
|
||||
}
|
||||
|
||||
debug_return_bool(signo == 0);
|
||||
debug_return_bool(group_stop);
|
||||
}
|
||||
#else
|
||||
/* STUB */
|
||||
@@ -1284,9 +1284,9 @@ have_seccomp_action(const char *action)
|
||||
|
||||
/* STUB */
|
||||
bool
|
||||
exec_ptrace_handled(pid_t pid, int status, void *intercept)
|
||||
exec_ptrace_stopped(pid_t pid, int status, void *intercept)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STUB */
|
||||
|
@@ -1100,7 +1100,7 @@ handle_sigchld_pty(struct exec_closure_pty *ec)
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
if (pid != ec->monitor_pid) {
|
||||
if (ISSET(ec->details->flags, CD_USE_PTRACE))
|
||||
exec_ptrace_handled(pid, status, ec->intercept);
|
||||
exec_ptrace_stopped(pid, status, ec->intercept);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -210,7 +210,13 @@ main(int argc, char *argv[])
|
||||
if (pid == child)
|
||||
return WTERMSIG(status) | 128;
|
||||
} else if (WIFSTOPPED(status)) {
|
||||
exec_ptrace_handled(pid, status, &closure);
|
||||
if (exec_ptrace_stopped(pid, status, &closure)) {
|
||||
if (pid == child) {
|
||||
/* TODO - terminal pgrp switching like exec_nopty.c. */
|
||||
kill(getpid(), WSTOPSIG(status));
|
||||
kill(child, SIGCONT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sudo_fatalx("%d: unknown status 0x%x", pid, status);
|
||||
}
|
||||
|
@@ -147,7 +147,7 @@ bool utmp_logout(const char *line, int status);
|
||||
char **sudo_preload_dso(char *envp[], const char *dso_file, int intercept_fd);
|
||||
|
||||
/* exec_ptrace.c */
|
||||
bool exec_ptrace_handled(pid_t pid, int status, void *intercept);
|
||||
bool exec_ptrace_stopped(pid_t pid, int status, void *intercept);
|
||||
bool set_exec_filter(void);
|
||||
int exec_ptrace_seize(pid_t child);
|
||||
|
||||
|
Reference in New Issue
Block a user