If the process is already being traced, just resume it and clear flags.

This makes it possible to run sudo in ptrace intercept mode from within
a shell (or other process) that is already being traced by sudo.
This commit is contained in:
Todd C. Miller
2022-05-03 13:34:40 -06:00
parent cc52ab770c
commit e84fdd99fd
4 changed files with 46 additions and 20 deletions

View File

@@ -476,18 +476,21 @@ exec_nopty(struct command_details *details, struct command_status *cstat)
fill_exec_closure_nopty(&ec, cstat, details, errpipe[0]);
if (ISSET(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS)) {
bool success = true;
int rc = 1;
/* Create event and closure for intercept mode. */
ec.intercept = intercept_setup(intercept_sv[0], ec.evbase, details);
if (ec.intercept == NULL) {
success = false;
rc = -1;
} else if (ISSET(details->flags, CD_USE_PTRACE)) {
/* Seize control of the command using ptrace(2). */
if (!exec_ptrace_seize(ec.cmnd_pid))
success = false;
/* Try to seize control of the command using ptrace(2). */
rc = exec_ptrace_seize(ec.cmnd_pid);
if (rc == 0) {
/* There is another tracer present. */
CLR(details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS|CD_USE_PTRACE);
}
if (!success)
}
if (rc == -1)
terminate_command(ec.cmnd_pid, true);
}

View File

@@ -611,27 +611,43 @@ set_exec_filter(void)
/*
* Seize control of the specified child process which must be in
* ptrace wait. Returns true on success and false on failure.
* ptrace wait. Returns true on success, false if child is already
* being traced and -1 on error.
*/
bool
int
exec_ptrace_seize(pid_t child)
{
const long ptrace_opts = PTRACE_O_TRACESECCOMP|PTRACE_O_TRACECLONE|
PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK;
int ret = -1;
int status;
debug_decl(exec_ptrace_seize, SUDO_DEBUG_UTIL);
/* Seize control of the child process. */
if (ptrace(PTRACE_SEIZE, child, NULL, ptrace_opts) == -1) {
/*
* If the process is already being traced, we will get EPERM.
* We don't treat that as a fatal error since we want it to be
* possible to run sudo inside a sudo shell with intercept enabled.
*/
if (errno != EPERM) {
sudo_warn("ptrace(PTRACE_SEIZE, %d, NULL, 0x%lx)", (int)child,
ptrace_opts);
debug_return_bool(false);
goto done;
}
sudo_debug_printf(SUDO_DEBUG_WARN,
"%s: unable to trace process %d, already being traced?",
__func__, (int)child);
ret = false;
}
/* The child is suspended waiting for SIGUSR1, wake it up. */
if (kill(child, SIGUSR1) == -1) {
sudo_warn("kill(%d, SIGUSR1)", child);
debug_return_bool(false);
goto done;
}
if (!ret)
goto done;
/* Wait for the child to enter trace stop and continue it. */
for (;;) {
@@ -640,18 +656,21 @@ exec_ptrace_seize(pid_t child)
if (errno == EINTR)
continue;
sudo_warn(U_("%s: %s"), __func__, "waitpid");
debug_return_bool(false);
goto done;
}
if (!WIFSTOPPED(status)) {
sudo_warnx(U_("process %d exited unexpectedly"), (int)child);
debug_return_bool(false);
goto done;
}
if (ptrace(PTRACE_CONT, child, NULL, (long)SIGUSR1) == -1) {
sudo_warn("ptrace(PTRACE_CONT, %d, NULL, SIGUSR1)", (int)child);
debug_return_bool(false);
goto done;
}
debug_return_bool(true);
ret = true;
done:
debug_return_int(ret);
}
/*
@@ -921,7 +940,7 @@ exec_ptrace_handled(pid_t pid, int status, void *intercept)
}
/* STUB */
bool
int
exec_ptrace_seize(pid_t child)
{
return true;

View File

@@ -994,8 +994,12 @@ backchannel_cb(int fd, int what, void *v)
sudo_debug_printf(SUDO_DEBUG_INFO, "executed %s, pid %d",
ec->details->command, (int)ec->cmnd_pid);
if (ISSET(ec->details->flags, CD_USE_PTRACE)) {
/* Seize control of the command using ptrace(2). */
if (!exec_ptrace_seize(ec->cmnd_pid)) {
/* Try to seize control of the command using ptrace(2). */
int rc = exec_ptrace_seize(ec->cmnd_pid);
if (rc == 0) {
/* There is another tracer present. */
CLR(ec->details->flags, CD_INTERCEPT|CD_LOG_SUBCMDS|CD_USE_PTRACE);
} else if (rc == -1) {
if (ec->cstat->type == CMD_INVALID) {
ec->cstat->type = CMD_ERRNO;
ec->cstat->val = errno;

View File

@@ -146,8 +146,8 @@ 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_seize(pid_t child);
bool have_seccomp_action(const char *action);
bool set_exec_filter(void);
int exec_ptrace_seize(pid_t child);
#endif /* SUDO_EXEC_H */