diff options
Diffstat (limited to 'sctrace.c')
-rw-r--r-- | sctrace.c | 314 |
1 files changed, 198 insertions, 116 deletions
@@ -3,7 +3,7 @@ char *argv0; -static unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC; +static unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT; _Noreturn static void @@ -16,129 +16,208 @@ usage(void) static void -handle_syscall(struct process *proc) +fetch_systemcall(struct process *proc, struct user_regs_struct *regs) { - struct user_regs_struct regs; - - switch ((int)proc->state) { - default: - /* Get system call arguments */ - if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, ®s))) - eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); - proc->scall = regs.SYSCALL_NUM_REG; + if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, regs))) + eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); + proc->scall = regs->SYSCALL_NUM_REG; #ifdef CHECK_ARCHITECTURE - CHECK_ARCHITECTURE(proc, ®s); - proc->scall ^= proc->scall_xor; + CHECK_ARCHITECTURE(proc, regs); + proc->scall ^= proc->scall_xor; #endif - GET_SYSCALL_ARGUMENTS(proc, ®s); - memset(proc->save, 0, sizeof(proc->save)); + GET_SYSCALL_ARGUMENTS(proc, regs); + memset(proc->save, 0, sizeof(proc->save)); +} - /* Print system call */ - print_systemcall(proc); - /* Run system call */ - if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) - eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); +static void +fetch_systemcall_result(struct process *proc, struct user_regs_struct *regs) +{ + if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, regs))) + eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); + proc->ret = regs->SYSCALL_RET_REG; +} - proc->state = Syscall; - break; - case Syscall: - case CloneParent: - case ForkParent: - /* Get system call result */ - if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, ®s))) - eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); +static void +enter_systemcall(struct process *proc) +{ + if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) + eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); +} - /* Get or set return */ - if (proc->state == Syscall) { - proc->ret = regs.SYSCALL_RET_REG; - } else { - regs.SYSCALL_RET_REG = proc->ret; - if (ptrace(PTRACE_SETREGS, proc->pid, REGARGS(NULL, ®s))) - eprintf("ptrace PTRACE_SETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); - if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) - eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); - } - /* Print system call result */ - print_systemcall_exit(proc); +static void +leave_systemcall(struct process *proc) +{ + if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) + eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); +} - proc->silent_until_execed -= (proc->silent_until_execed == 1); - /* Make process continue and stop at next syscall */ - if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) - eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); +static void +restart_systemcall(struct process *proc, int sig) +{ + if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, sig)) + eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, sig); +} - proc->state = Normal; - break; - case Exec: - proc->silent_until_execed -= (proc->silent_until_execed == 2); - FALL_THROUGH - /* fall through */ - case VforkParent: - if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) - eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); - proc->state = Syscall; +static void +vfork_start(struct process *proc) +{ + tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n"); +} + + +static void +vfork_done(struct process *proc, const char *reason) +{ + tprintf(proc, "\nProcess continues due to %s of vfork child\n", reason); +} + + +static void +process_exiting(struct process *proc, int status) +{ + (void) proc; + (void) status; +} + + +static void +process_exited(struct process *proc, int status) +{ + if (WIFEXITED(status)) { + tprintf(proc, "\nProcess exited%s with value %i%s\n", + proc->state == Zombie ? "" : ", without knell", + WEXITSTATUS(status), WCOREDUMP(status) ? ", core dumped" : ""); + } else { + tprintf(proc, "\nProcess terminated%s by signal %i (%s: %s)%s\n", + proc->state == Zombie ? "" : ", without knell", + WTERMSIG(status), get_signum_name(WTERMSIG(status)), + strsignal(WTERMSIG(status)), WCOREDUMP(status) ? ", core dumped" : ""); + } +} + + +static void +process_signalled(struct process *proc, int sig, int stopped) +{ + tprintf(proc, "\nProcess %s signal %i (%s: %s)\n", + stopped ? "stopped by" : "received", + sig, get_signum_name(sig), strsignal(sig)); +} + + +static void +process_continued(struct process *proc, int status) +{ + tprintf(proc, "\n%s continued, presumably by signal %i (SIGCONT: %s)\n", + proc->state == Zombie ? "Zombie process" : "Process", SIGCONT, strsignal(SIGCONT)); + (void) status; +} + + +static void +restart_process(struct process *proc, int cmd, int sig) +{ + if (ptrace(cmd, proc->pid, NULL, sig)) { + eprintf("ptrace %s %ju NULL %i:", + cmd == PTRACE_CONT ? "PTRACE_CONT" : + cmd == PTRACE_LISTEN ? "PTRACE_LISTN" : + cmd == PTRACE_SYSEMU ? "PTRACE_SYSEMU" : + cmd == PTRACE_SYSCALL ? "PTRACE_SYSCALL" : "???", + (uintmax_t)proc->pid, sig); + } +} + + +static void +process_created(struct process *proc, struct process *parent) +{ + tprintf(proc, "\nTracing new process with parent %ju\n", (uintmax_t)parent->pid); + tprintf(proc, "= 0\n"); +} + + +static void +zombie_stopped(struct process *proc, int status) +{ + tprintf(proc, "\nReceived unexpected event on zombie process\n"); + (void) status; +} + + +static void +handle_syscall(struct process *proc) +{ + struct user_regs_struct regs; + + switch ((int)proc->state) { + case UserSpace: + fetch_systemcall(proc, ®s); + print_systemcall(proc); + enter_systemcall(proc); + proc->state = KernelSpace; break; - case CloneChild: - case ForkChild: - case VforkChild: - tprintf(proc, "= 0\n"); - proc->state = Normal; + case KernelSpace: + fetch_systemcall_result(proc, ®s); + if (!proc->ignore_until_execed) + print_systemcall_exit(proc); + else + proc->ignore_until_execed -= (proc->ignore_until_execed == 1); + leave_systemcall(proc); + proc->state = UserSpace; break; + + default: + abort(); } } +static unsigned long int +get_event_msg(struct process *proc) +{ + unsigned long int event; + if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event)) + eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid); + return event; +} + + static void handle_event(struct process *proc, int status) { - int trace_event, sig; - unsigned long int event; struct process *proc2; - - sig = WSTOPSIG(status); - trace_event = status >> 16; + int sig = WSTOPSIG(status); + int trace_event = status >> 16; switch (trace_event) { case PTRACE_EVENT_VFORK: - tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n"); - FALL_THROUGH - /* fall through */ case PTRACE_EVENT_FORK: case PTRACE_EVENT_CLONE: - if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event)) - eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid); - proc2 = add_process((pid_t)event, trace_options); - if (trace_event == PTRACE_EVENT_CLONE) - proc2->thread_leader = proc->pid; - proc->ret = event; + proc2 = add_process((pid_t)get_event_msg(proc), trace_event == PTRACE_EVENT_CLONE ? proc->pid : 0, trace_options); if (trace_event == PTRACE_EVENT_VFORK) { + vfork_start(proc); proc2->continue_on_exit = proc; proc->vfork_waiting_on = proc2; - proc->state = VforkParent; - } else { - proc->state = trace_event == PTRACE_EVENT_CLONE ? CloneParent : ForkParent; - handle_syscall(proc); } - tprintf(proc2, "\nTracing new process with parent %ju\n", (uintmax_t)proc->pid); - proc2->state = trace_event == PTRACE_EVENT_FORK ? ForkChild : - trace_event == PTRACE_EVENT_VFORK ? VforkChild : CloneChild; - handle_syscall(proc2); + process_created(proc2, proc); + restart_systemcall(proc2, 0); + restart_systemcall(proc, 0); break; case PTRACE_EVENT_EXEC: - proc->state = Exec; - handle_syscall(proc); + proc->ignore_until_execed -= (proc->ignore_until_execed == 2); + restart_systemcall(proc, 0); proc2 = proc->continue_on_exit; if (proc2) { proc->continue_on_exit = NULL; proc2->vfork_waiting_on = NULL; - tprintf(proc2, "\nProcess continues due to exec(2) of vfork child\n"); - handle_syscall(proc2); + vfork_done(proc2, "exec(2)"); } break; @@ -148,27 +227,34 @@ handle_event(struct process *proc, int status) case SIGTSTP: case SIGTTIN: case SIGTTOU: - stop_signal: - tprintf(proc, "\nProcess stopped by signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig)); - if (ptrace(PTRACE_LISTEN, proc->pid, NULL, 0)) - eprintf("ptrace PTRACE_LISTEN %ju NULL 0:", (uintmax_t)proc->pid); + process_signalled(proc, sig, 1); + restart_process(proc, PTRACE_LISTEN, 0); break; default: - if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0)) - eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid); + tprintf(proc, "\nTRACE_EVENT_STOP with signal %i (%s: %s)\n", + WTERMSIG(status), get_signum_name(WTERMSIG(status)), + strsignal(WTERMSIG(status))); + restart_systemcall(proc, 0); break; } break; + case PTRACE_EVENT_EXIT: + process_exiting(proc, (int)get_event_msg(proc)); + proc->state = Zombie; + restart_process(proc, PTRACE_CONT, 0); + break; + + case PTRACE_EVENT_VFORK_DONE: + restart_systemcall(proc, 0); + break; + default: abort(); case 0: - if (ptrace(PTRACE_GETSIGINFO, proc->pid, 0, &(siginfo_t){0})) - goto stop_signal; - tprintf(proc, "\nProcess received signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig)); - if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, sig)) - eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, sig); + process_signalled(proc, sig, 0); + restart_systemcall(proc, sig); break; } } @@ -182,7 +268,7 @@ main(int argc, char **argv) FILE *outfp = stderr; int status, exit_code = 0, with_argv0 = 0, multiprocess = 0, i; char *arg; - struct process *proc, *proc2; + struct process *proc; struct sigaction sa; sigset_t sm; @@ -212,6 +298,7 @@ main(int argc, char **argv) case 'f': trace_options |= PTRACE_O_TRACEFORK; trace_options |= PTRACE_O_TRACEVFORK; + trace_options |= PTRACE_O_TRACEVFORKDONE; FALL_THROUGH /* fall through */ case 't': @@ -224,6 +311,9 @@ main(int argc, char **argv) if (!argc) usage(); + if (prctl(PR_SET_CHILD_SUBREAPER, 1)) + weprintf("prctl PR_SET_CHILD_SUBREAPER 1:"); + orig_pid = fork(); switch (orig_pid) { case -1: @@ -247,10 +337,12 @@ main(int argc, char **argv) outfp = outfile ? xfopen(outfile, "wb") : stderr; setup_trace_output(outfp, multiprocess); init_process_list(); - add_process(orig_pid, trace_options)->silent_until_execed = 2; + proc = add_process(orig_pid, 0, trace_options); + proc->ignore_until_execed = 2; + restart_systemcall(proc, 0); for (;;) { - pid = waitpid(-1, &status, __WALL | WCONTINUED); + pid = waitpid(-1, &status, __WALL | WCONTINUED); /* TODO WCONTINUED should require waitid */ if (pid < 0) { if (errno == ECHILD) break; @@ -264,32 +356,21 @@ main(int argc, char **argv) continue; if (WIFSTOPPED(status)) { - if (WSTOPSIG(status) == (SIGTRAP | 0x80)) + if (proc->state == Zombie) + zombie_stopped(proc, status); + else if (WSTOPSIG(status) == (SIGTRAP | 0x80)) handle_syscall(proc); else handle_event(proc, status); } else if (WIFCONTINUED(status)) { - tprintf(proc, "\nProcess continued, presumably by signal %i (SIGCONT: %s)\n", SIGCONT, strsignal(SIGCONT)); + process_continued(proc, status); } else { if (pid == orig_pid) exit_code = status; - if (WIFEXITED(status)) { - tprintf(proc, "\nProcess exited with value %i%s\n", WEXITSTATUS(status), - WCOREDUMP(status) ? ", core dumped" : ""); - } else { - tprintf(proc, "\nProcess terminated by signal %i (%s: %s)%s\n", WTERMSIG(status), - get_signum_name(WTERMSIG(status)), strsignal(WTERMSIG(status)), - WCOREDUMP(status) ? ", core dumped" : ""); - } - proc2 = proc->continue_on_exit; + process_exited(proc, status); + if (proc->continue_on_exit) + vfork_done(proc->continue_on_exit, WIFEXITED(status) ? "exit" : "abnormal termination"); remove_process(proc); - if (proc2) { - if (WIFEXITED(status)) - tprintf(proc2, "\nProcess continues due to exit of vfork child\n"); - else - tprintf(proc2, "\nProcess continues due to abnormal termination of vfork child\n"); - handle_syscall(proc2); - } } fflush(outfp); @@ -301,6 +382,7 @@ main(int argc, char **argv) weprintf("Copying exit from %s\n", multiprocess ? "original tracee" : "tracee"); if (WIFSIGNALED(exit_code)) { + prctl(PR_SET_DUMPABLE, 0); exit_code = WTERMSIG(exit_code); raise(exit_code); return exit_code + 128; |