aboutsummaryrefslogtreecommitdiffstats
path: root/sctrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'sctrace.c')
-rw-r--r--sctrace.c314
1 files changed, 198 insertions, 116 deletions
diff --git a/sctrace.c b/sctrace.c
index 976624d..fe1ff5e 100644
--- a/sctrace.c
+++ b/sctrace.c
@@ -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, &regs)))
- 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, &regs);
- proc->scall ^= proc->scall_xor;
+ CHECK_ARCHITECTURE(proc, regs);
+ proc->scall ^= proc->scall_xor;
#endif
- GET_SYSCALL_ARGUMENTS(proc, &regs);
- 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, &regs)))
- 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, &regs)))
- 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, &regs);
+ 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, &regs);
+ 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;