ptrace: support tracing from non-parent process

This commit is contained in:
K Lange 2021-09-24 19:15:06 +09:00
parent 41ba264cdc
commit aa8fdab570
5 changed files with 194 additions and 100 deletions

View File

@ -30,6 +30,8 @@
static char * last_command = NULL;
static char * binary_path = NULL;
static FILE * binary_obj = NULL;
static pid_t binary_pid = 0;
static int binary_is_child = 0;
struct regs {
uintptr_t r15, r14, r13, r12;
@ -484,8 +486,10 @@ static void show_commandline(pid_t pid, int status, struct regs * regs) {
}
_exitDebugger:
ptrace(PTRACE_CONT, pid, NULL, (void*)(uintptr_t)SIGKILL);
fprintf(stderr, "Exiting.\n");
if (binary_is_child) {
fprintf(stderr, "Terminating child process '%d'.\n", pid);
ptrace(PTRACE_CONT, pid, NULL, (void*)(uintptr_t)SIGKILL);
}
exit(0);
}
@ -544,10 +548,19 @@ static char * sig_to_str(int signum) {
return _buf;
}
static void pass_sig(int sig) {
kill(binary_pid, sig);
signal(SIGINT, pass_sig);
}
int main(int argc, char * argv[]) {
pid_t target_pid = 0;
int opt;
while ((opt = getopt(argc, argv, "o:")) != -1) {
while ((opt = getopt(argc, argv, "o:p:h")) != -1) {
switch (opt) {
case 'p':
target_pid = atoi(optarg);
break;
case 'h':
return (usage(argv), 0);
case '?':
@ -580,63 +593,74 @@ int main(int argc, char * argv[]) {
/* Attempt to load symbol information... */
pid_t p = fork();
if (!p) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
if (target_pid) {
binary_pid = target_pid;
if (ptrace(PTRACE_ATTACH, binary_pid, NULL, NULL) < 0) {
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
return 1;
}
execv(binary_path, &argv[optind]);
return 1;
signal(SIGINT, pass_sig);
} else {
binary_is_child = 1;
binary_pid = fork();
if (!binary_pid) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
return 1;
}
execv(binary_path, &argv[optind]);
return 1;
}
signal(SIGINT, SIG_IGN);
}
while (1) {
int status = 0;
pid_t res = waitpid(p, &status, WSTOPPED);
while (1) {
int status = 0;
pid_t res = waitpid(binary_pid, &status, WSTOPPED);
if (res == 0) continue;
if (res == 0) continue;
if (res < 0) {
if (errno == EINTR) continue;
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
} else {
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) == SIGTRAP) {
/* Don't care about TRAP right now */
int event = (status >> 16) & 0xFF;
switch (event) {
case PTRACE_EVENT_SINGLESTEP: {
struct regs regs;
ptrace(PTRACE_GETREGS, res, NULL, &regs);
show_commandline(res, status, &regs);
}
break;
default:
//ptrace(PTRACE_SIGNALS_ONLY_PLZ, p, NULL, NULL);
ptrace(PTRACE_CONT, p, NULL, NULL);
break;
}
} else {
printf("Program received signal %s.\n", sig_to_str(WSTOPSIG(status)));
struct regs regs;
ptrace(PTRACE_GETREGS, res, NULL, &regs);
show_commandline(res, status, &regs);
if (res < 0) {
if (errno == EINTR) continue;
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
} else {
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) == SIGTRAP) {
/* Don't care about TRAP right now */
int event = (status >> 16) & 0xFF;
switch (event) {
case PTRACE_EVENT_SINGLESTEP: {
struct regs regs;
ptrace(PTRACE_GETREGS, res, NULL, &regs);
show_commandline(res, status, &regs);
}
break;
default:
//ptrace(PTRACE_SIGNALS_ONLY_PLZ, p, NULL, NULL);
ptrace(PTRACE_CONT, res, NULL, NULL);
break;
}
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "Process %d was killed by %s.\n", res, signal_names[WTERMSIG(status)]);
return 0;
} else if (WIFEXITED(status)) {
fprintf(stderr, "Process %d exited normally.\n", res);
return 0;
} else {
fprintf(stderr, "Unknown state?\n");
printf("Program received signal %s.\n", sig_to_str(WSTOPSIG(status)));
struct regs regs;
ptrace(PTRACE_GETREGS, res, NULL, &regs);
show_commandline(res, status, &regs);
}
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "Process %d was killed by %s.\n", res, signal_names[WTERMSIG(status)]);
return 0;
} else if (WIFEXITED(status)) {
fprintf(stderr, "Process %d exited normally.\n", res);
return 0;
} else {
fprintf(stderr, "Unknown state?\n");
}
}
}
return 0;
}

View File

@ -821,10 +821,11 @@ static void finish_syscall(pid_t pid, int syscall, struct regs * r) {
static int usage(char * argv[]) {
#define T_I "\033[3m"
#define T_O "\033[0m"
fprintf(stderr, "usage: %s [-o logfile] [-e trace=...] command...\n"
fprintf(stderr, "usage: %s [-o logfile] [-e trace=...] [-p PID] [command...]\n"
" -o logfile " T_I "Write tracing output to a file." T_O "\n"
" -h " T_I "Show this help text." T_O "\n"
" -e trace=... " T_I "Set tracing options." T_O "\n",
" -e trace=... " T_I "Set tracing options." T_O "\n"
" -p PID " T_I "Trace an existing process." T_O "\n",
argv[0]);
return 1;
}
@ -832,9 +833,13 @@ static int usage(char * argv[]) {
int main(int argc, char * argv[]) {
logfile = stdout;
pid_t p = 0;
int opt;
while ((opt = getopt(argc, argv, "ho:e:")) != -1) {
while ((opt = getopt(argc, argv, "ho:e:p:")) != -1) {
switch (opt) {
case 'p':
p = atoi(optarg);
break;
case 'o':
logfile = fopen(optarg, "w");
if (!logfile) {
@ -960,59 +965,66 @@ int main(int argc, char * argv[]) {
}
}
if (optind == argc) {
if (!p && optind == argc) {
return usage(argv);
}
pid_t p = fork();
if (!p) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
p = fork();
if (!p) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) {
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
return 1;
}
execvp(argv[optind], &argv[optind]);
return 1;
}
signal(SIGINT, SIG_IGN);
} else {
if (ptrace(PTRACE_ATTACH, p, NULL, NULL) < 0) {
fprintf(stderr, "%s: ptrace: %s\n", argv[0], strerror(errno));
return 1;
}
execvp(argv[optind], &argv[optind]);
return 1;
} else {
signal(SIGINT, SIG_IGN);
int previous_syscall = -1;
while (1) {
int status = 0;
pid_t res = waitpid(p, &status, WSTOPPED);
}
if (res < 0) {
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
} else {
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) == SIGTRAP) {
struct regs regs;
ptrace(PTRACE_GETREGS, p, NULL, &regs);
int previous_syscall = -1;
while (1) {
int status = 0;
pid_t res = waitpid(p, &status, WSTOPPED);
/* Event type */
int event = (status >> 16) & 0xFF;
switch (event) {
case PTRACE_EVENT_SYSCALL_ENTER:
previous_syscall = regs.rax;
handle_syscall(p, &regs);
break;
case PTRACE_EVENT_SYSCALL_EXIT:
finish_syscall(p, previous_syscall, &regs);
break;
default:
fprintf(logfile, "Unknown event.\n");
break;
}
ptrace(PTRACE_CONT, p, NULL, NULL);
} else {
fprintf(logfile, "--- %s ---\n", signal_names[WSTOPSIG(status)]);
ptrace(PTRACE_CONT, p, NULL, (void*)(uintptr_t)WSTOPSIG(status));
if (res < 0) {
fprintf(stderr, "%s: waitpid: %s\n", argv[0], strerror(errno));
} else {
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) == SIGTRAP) {
struct regs regs;
ptrace(PTRACE_GETREGS, p, NULL, &regs);
/* Event type */
int event = (status >> 16) & 0xFF;
switch (event) {
case PTRACE_EVENT_SYSCALL_ENTER:
previous_syscall = regs.rax;
handle_syscall(p, &regs);
break;
case PTRACE_EVENT_SYSCALL_EXIT:
finish_syscall(p, previous_syscall, &regs);
break;
default:
fprintf(logfile, "Unknown event.\n");
break;
}
} else if (WIFSIGNALED(status)) {
fprintf(logfile, "+++ killed by %s +++\n", signal_names[WTERMSIG(status)]);
return 0;
} else if (WIFEXITED(status)) {
fprintf(logfile, "+++ exited with %d +++\n", WEXITSTATUS(status));
return 0;
ptrace(PTRACE_CONT, p, NULL, NULL);
} else {
fprintf(logfile, "--- %s ---\n", signal_names[WSTOPSIG(status)]);
ptrace(PTRACE_CONT, p, NULL, (void*)(uintptr_t)WSTOPSIG(status));
}
} else if (WIFSIGNALED(status)) {
fprintf(logfile, "+++ killed by %s +++\n", signal_names[WTERMSIG(status)]);
return 0;
} else if (WIFEXITED(status)) {
fprintf(logfile, "+++ exited with %d +++\n", WEXITSTATUS(status));
return 0;
}
}
}

View File

@ -137,6 +137,7 @@ typedef struct process {
/* Tracing */
pid_t tracer;
spin_lock_t wait_lock;
list_t * tracees;
} process_t;
typedef struct {

View File

@ -527,6 +527,13 @@ void process_reap(process_t * proc) {
free(proc->signal_kstack);
}
if (proc->tracees) {
while (proc->tracees->length) {
free(list_pop(proc->tracees));
}
free(proc->tracees);
}
/* Unmark the stack bottom's fault detector */
mmu_frame_allocate(
mmu_get_page(proc->image.stack - KERNEL_STACK_SIZE, 0),
@ -963,6 +970,7 @@ int waitpid(int pid, int * status, int options) {
do {
volatile process_t * candidate = NULL;
int has_children = 0;
int is_parent = 0;
spin_lock(proc->wait_lock);
@ -975,6 +983,7 @@ int waitpid(int pid, int * status, int options) {
if (wait_candidate(proc, pid, options, child)) {
has_children = 1;
is_parent = 1;
if (child->flags & PROC_FLAG_FINISHED) {
candidate = child;
break;
@ -986,6 +995,19 @@ int waitpid(int pid, int * status, int options) {
}
}
if (!candidate && proc->tracees) {
foreach(node, proc->tracees) {
process_t * child = node->value;
if (wait_candidate(proc,pid,options,child)) {
has_children = 1;
if (child->flags & PROC_FLAG_SUSPENDED) {
candidate = child;
break;
}
}
}
}
if (!has_children) {
/* No valid children matching this description */
spin_unlock(proc->wait_lock);
@ -998,7 +1020,7 @@ int waitpid(int pid, int * status, int options) {
*status = candidate->status;
}
int pid = candidate->id;
if (candidate->flags & PROC_FLAG_FINISHED) {
if (is_parent && (candidate->flags & PROC_FLAG_FINISHED)) {
while (*((volatile int *)&candidate->flags) & PROC_FLAG_RUNNING);
proc->time_children += candidate->time_children + candidate->time_total;
proc->time_sys_children += candidate->time_sys_children + candidate->time_sys;
@ -1211,6 +1233,25 @@ void task_exit(int retval) {
}
}
if (this_core->current_process->tracees) {
spin_lock(this_core->current_process->wait_lock);
while (this_core->current_process->tracees->length) {
node_t * n = list_pop(this_core->current_process->tracees);
process_t * tracee = n->value;
free(n);
if (is_valid_process(tracee)) {
tracee->tracer = 0;
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_TRACE_SIGNALS | PROC_FLAG_TRACE_SYSCALLS));
if (tracee->flags & PROC_FLAG_SUSPENDED) {
tracee->status = 0;
__sync_and_and_fetch(&tracee->flags, ~(PROC_FLAG_SUSPENDED));
make_process_ready(tracee);
}
}
}
spin_unlock(this_core->current_process->wait_lock);
}
update_process_times(1);
process_t * parent = process_get_parent((process_t *)this_core->current_process);

View File

@ -12,22 +12,38 @@
#include <kernel/arch/x86_64/regs.h>
#include <kernel/arch/x86_64/mmu.h>
static void _ptrace_trace(process_t * tracer, process_t * tracee) {
spin_lock(tracer->wait_lock);
__sync_or_and_fetch(&tracee->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
if (!tracer->tracees) {
tracer->tracees = list_create("debug tracees", tracer);
}
list_insert(tracer->tracees, tracee);
tracee->tracer = tracer->id;
spin_unlock(tracer->wait_lock);
}
long ptrace_attach(pid_t pid) {
process_t * tracer = (process_t *)this_core->current_process;
process_t * tracee = process_from_pid(pid);
if (!tracee) return -ESRCH;
if (this_core->current_process->user != 0 && this_core->current_process->user != tracee->user) return -EPERM;
__sync_or_and_fetch(&tracee->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
tracee->tracer = this_core->current_process->id;
send_signal(pid, SIGSTOP, 1);
if (tracer->user != 0 && tracer->user != tracee->user) return -EPERM;
_ptrace_trace(tracer, tracee);
return 0;
}
long ptrace_self(void) {
process_t * parent = process_get_parent((process_t*)this_core->current_process);
if (!parent) return -EINVAL;
process_t * tracee = (process_t*)this_core->current_process;
process_t * tracer = process_get_parent(tracee);
if (!tracer) return -EINVAL;
__sync_or_and_fetch(&this_core->current_process->flags, (PROC_FLAG_TRACE_SYSCALLS | PROC_FLAG_TRACE_SIGNALS));
this_core->current_process->tracer = parent->id;
_ptrace_trace(tracer, tracee);
return 0;
}