diff options
author | Malfurious <m@lfurio.us> | 2023-10-06 04:55:18 -0400 |
---|---|---|
committer | Malfurious <m@lfurio.us> | 2024-04-24 13:31:08 -0400 |
commit | 66db439988aa07828593aac109f5690bb48f2dc9 (patch) | |
tree | 8796e059228b9d67a677be93036305b9bd293b18 | |
parent | 46f72be263cf29688f684e90f2e149e5c911016b (diff) | |
download | misplays-66db439988aa07828593aac109f5690bb48f2dc9.tar.gz misplays-66db439988aa07828593aac109f5690bb48f2dc9.zip |
Independent thread control refactor
Signed-off-by: Malfurious <m@lfurio.us>
-rw-r--r-- | debugger.c | 412 | ||||
-rw-r--r-- | debugger.h | 6 | ||||
-rw-r--r-- | misplays.c | 5 |
3 files changed, 203 insertions, 220 deletions
@@ -19,10 +19,35 @@ static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD; static const int PTRACE_CHILD_OPTIONS = PTRACE_OPTIONS | PTRACE_O_EXITKILL; -static const int STOP_ALL_ON_EVENT = 1; +//static const int STOP_ALL_ON_EVENT = 1; static const useconds_t SCHEDULER_DELAY = 100000; static const unsigned int BREAKPOINT_INSN = 0xcc; +static struct process *new_process(pid_t id, int child) { + struct process *proc = xmalloc(sizeof(*proc)); + proc->id = id; + proc->child = child; + list_init(&proc->breakpoints); + list_init(&proc->threads); + memset(proc->status, 0, sizeof(proc->status)); + return proc; +} + +static struct thread *new_thread(struct process *proc, pid_t id) { + struct thread *th = xmalloc(sizeof(*th)); + th->proc = proc; + list_init(&th->states); + th->state = NULL; + th->clearstates = 0; + th->id = id; + th->stopped = 0; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "RUNNING"); + return th; +} + static struct thread *thread_by_id(struct process *proc, pid_t id) { struct list *threads = &proc->threads; for (struct thread *th = threads->head; th != threads->end; th = th->next) { @@ -132,7 +157,7 @@ static void free_states(struct thread *th) { static void capture_state(struct process *proc) { struct list *threads = &proc->threads; for (struct thread *th = threads->head; th != threads->end; th = th->next) { - if (!th->shouldcont) { + if (!th->state) { if (th->clearstates) { free_states(th); } @@ -173,15 +198,17 @@ static void capture_state(struct process *proc) { } } +static int wait_thread(struct thread *th); static void interrupt_all_threads(struct process *proc) { struct list *threads = &proc->threads; for (struct thread *th = threads->head; th != threads->end; th = th->next) { if (!th->stopped) { ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); - while (!dbg_wait(th, 0)) {} - } - if (STOP_ALL_ON_EVENT) { - th->shouldcont = 0; + while (!wait_thread(th)) {} + //if (STOP_ALL_ON_EVENT) { + // th->cont = 0; + // th->shouldcont = 0; + //} } } } @@ -193,53 +220,157 @@ static void continue_all_threads(struct process *proc) { ptrace(PTRACE_CONT, th->id, NULL, th->signal); th->stopped = 0; th->signal = 0; - th->cont = 0; - th->shouldcont = 2; + th->donext = 0; + th->doing = PTRACE_CONT; strcpy(th->status, "RUNNING"); th->clearstates = 1; } } } -/* 0: stop 1: singlestep 2: cont 3: syscall */ -/* this is simplified, surely incorrect, and needs updated once testable */ static void resume_threads(struct process *proc) { struct list *threads = &proc->threads; + + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (th->doing == PTRACE_SINGLESTEP) { + ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); + th->stopped = 0; + th->signal = 0; + strcpy(th->status, "RUNNING"); + } + } + for (struct thread *th = threads->head; th != threads->end; th = th->next) { - if (th->shouldcont) { + if (th->doing && th->doing != PTRACE_SINGLESTEP) { install_breakpoints(th); - ptrace(PTRACE_CONT, th->id, NULL, th->signal); + ptrace(th->doing, th->id, NULL, th->signal); th->stopped = 0; th->signal = 0; - th->cont = 0; strcpy(th->status, "RUNNING"); } } } -static struct process *new_process(pid_t id, int child) { - struct process *proc = xmalloc(sizeof(*proc)); - proc->id = id; - proc->child = child; - list_init(&proc->breakpoints); - list_init(&proc->threads); - memset(proc->status, 0, sizeof(proc->status)); - return proc; -} +static int wait_thread(struct thread *th) { + if (th->id <= 0) { + return -1; + } else if (th->stopped) { + return 1; + } -static struct thread *new_thread(struct process *proc, pid_t id) { - struct thread *th = xmalloc(sizeof(*th)); - th->proc = proc; - list_init(&th->states); - th->state = NULL; - th->clearstates = 0; - th->id = id; - th->stopped = 0; - th->signal = 0; - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "RUNNING"); - return th; + int status; + if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) { + return 0; + } + + if (WIFEXITED(status)) { + th->id = 0; + th->stopped = 1; + th->signal = WEXITSTATUS(status); + th->doing = 0; + th->donext = 0; + strcpy(th->status, "EXITED"); + return 1; + } else if (WIFSIGNALED(status)) { + th->id = 0; + th->stopped = 1; + th->signal = WTERMSIG(status); + th->doing = 0; + th->donext = 0; + strcpy(th->status, "TERMINATED"); + return 1; + } else if (WIFSTOPPED(status)) { + struct thread *eventth; + unsigned long eventmsg; + ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg); + + /* todo: process status messages */ + switch (status >> 8) { + /* todo: other ptrace event stops */ + case SIGTRAP | (PTRACE_EVENT_CLONE << 8): + eventth = new_thread(th->proc, eventmsg); + list_insert(th->proc->threads.end, eventth); + while (!wait_thread(eventth)) {} + + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "CLONE"); + th->state = NULL; + return 1; + + case SIGTRAP | (PTRACE_EVENT_EXEC << 8): + eventth = thread_by_id(th->proc, eventmsg); + if (eventth->id != th->id) { + eventth->id = 0; + eventth->stopped = 1; + eventth->signal = 0; + eventth->doing = 0; + eventth->donext = 0; + strcpy(eventth->status, "EXITED"); + } + + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "EXECVE"); + th->state = NULL; + return 1; + + case SIGTRAP | (PTRACE_EVENT_STOP << 8): + th->stopped = 1; + th->signal = 0; + strcpy(th->status, "STOPPED"); + if (!th->doing) { + th->state = NULL; + } + return 1; + + case SIGTRAP | 0x80: + th->stopped = 1; + th->signal = 0; + th->doing = 0; + th->donext = 0; + strcpy(th->status, "SYSCALL"); + th->state = NULL; + return 1; + + case SIGTRAP: + th->stopped = 1; + th->signal = 0; + + int restart; + int bp = detect_breakpoint(th, &restart); + strcpy(th->status, (bp ? "BREAKPOINT" : "STEP")); + + if (restart) { + th->donext = th->doing; + th->doing = (th->doing ? PTRACE_SINGLESTEP : 0); + } else { + th->doing = th->donext; + th->donext = 0; + } + + if (!th->doing) { + th->state = NULL; + } + + return 1; + + default: + th->stopped = 1; + th->signal = WSTOPSIG(status); + th->doing = 0; + th->donext = 0; + strcpy(th->status, "SIGNAL DELIVERY"); + th->state = NULL; + return 1; + } + } + + return -1; } struct breakpoint *add_breakpoint(struct process *proc, unsigned long address) { @@ -343,165 +474,26 @@ int dbg_free(struct thread *th) { return -1; } -int dbg_wait(struct thread *th, int primary) { - if (th->id <= 0) { - return -1; - } else if (th->stopped) { - return 1; - } - - int status; - if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) { - return 0; - } - - if (WIFEXITED(status)) { - th->id = 0; - th->stopped = 1; - th->signal = WEXITSTATUS(status); - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "EXITED"); - return 1; - } - - if (WIFSIGNALED(status)) { - th->id = 0; - th->stopped = 1; - th->signal = WTERMSIG(status); - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "TERMINATED"); - return 1; - } - - struct thread *newth; // event thread - unsigned long eventmsg; - ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg); - - /* todo: process status messages */ - if (WIFSTOPPED(status)) { - switch (status >> 8) { - /* todo: other ptrace event stops */ - case SIGTRAP | (PTRACE_EVENT_CLONE << 8): - newth = new_thread(th->proc, eventmsg); - list_insert(th->proc->threads.end, newth); - while (!dbg_wait(newth, 0)) {} - - th->stopped = 1; - th->signal = 0; - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "CLONE"); - th->state = NULL; - - if (primary) { - interrupt_all_threads(th->proc); - uninstall_breakpoints(th); - capture_state(th->proc); - resume_threads(th->proc); - } - - return 1; - - case SIGTRAP | (PTRACE_EVENT_EXEC << 8): - if ((pid_t)eventmsg != th->id) { - newth = thread_by_id(th->proc, eventmsg); - newth->id = 0; - newth->stopped = 1; - newth->signal = 0; - newth->cont = 0; - newth->shouldcont = 0; - strcpy(newth->status, "EXITED"); - } - - th->stopped = 1; - th->signal = 0; - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "EXECVE"); - th->state = NULL; - - if (primary) { - interrupt_all_threads(th->proc); - uninstall_breakpoints(th); - capture_state(th->proc); - resume_threads(th->proc); - } - - return 1; - - case SIGTRAP | (PTRACE_EVENT_STOP << 8): - th->stopped = 1; - th->signal = 0; - th->cont = 0; - strcpy(th->status, "STOPPED"); - - if (primary) { - interrupt_all_threads(th->proc); - uninstall_breakpoints(th); - capture_state(th->proc); - resume_threads(th->proc); - } - - return th->stopped; - - case SIGTRAP | 0x80: - th->stopped = 1; - th->signal = 0; - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "SYSCALL"); - th->state = NULL; - - if (primary) { - interrupt_all_threads(th->proc); - uninstall_breakpoints(th); - capture_state(th->proc); - resume_threads(th->proc); - } - - return 1; +void dbg_sync(struct process *proc) { + struct thread *acted = NULL; - case SIGTRAP: - th->stopped = 1; - th->signal = 0; - - int restart; - int bp = detect_breakpoint(th, &restart); - strcpy(th->status, (bp ? "BREAKPOINT" : "STEP")); - //th->shouldcont = (b && b->enabled == 0); - th->shouldcont = (restart ? 2 : 0); - - if (primary) { - interrupt_all_threads(th->proc); - uninstall_breakpoints(th); - capture_state(th->proc); - resume_threads(th->proc); - } - - return th->stopped; - - default: - th->stopped = 1; - th->signal = WSTOPSIG(status); - th->cont = 0; - th->shouldcont = 0; - strcpy(th->status, "SIGNAL DELIVERY"); - th->state = NULL; - - if (primary) { - interrupt_all_threads(th->proc); - uninstall_breakpoints(th); - capture_state(th->proc); - resume_threads(th->proc); - } - - return 1; + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + if (th->id > 0) { + if (!th->stopped && wait_thread(th)) { + acted = th; + } else if (th->stopped && th->doing) { + acted = th; + } } } - return -1; + if (acted) { + interrupt_all_threads(proc); + uninstall_breakpoints(acted); + capture_state(proc); + resume_threads(proc); + } } int dbg_intr(struct thread *th) { @@ -509,8 +501,11 @@ int dbg_intr(struct thread *th) { return -1; } + /* todo: move this into dbg_sync? */ + /* probably not */ ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); - th->shouldcont = 0; + th->doing = 0; + th->donext = 0; return 0; } @@ -519,12 +514,8 @@ int dbg_cont(struct thread *th) { return -1; } - ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); - th->stopped = 0; - th->signal = 0; - th->cont = PTRACE_CONT; - th->shouldcont = 1; - strcpy(th->status, "RUNNING"); + th->doing = PTRACE_SINGLESTEP; + th->donext = PTRACE_CONT; th->clearstates = 1; return 0; } @@ -534,12 +525,8 @@ int dbg_syscall(struct thread *th) { return -1; } - ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); - th->stopped = 0; - th->signal = 0; - th->cont = PTRACE_SYSCALL; - th->shouldcont = 1; - strcpy(th->status, "RUNNING"); + th->doing = PTRACE_SINGLESTEP; + th->donext = PTRACE_SYSCALL; th->clearstates = 1; return 0; } @@ -558,12 +545,8 @@ int dbg_stepin(struct thread *th) { return -1; } - ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); - th->stopped = 0; - th->signal = 0; - th->cont = PTRACE_SINGLESTEP; - th->shouldcont = 1; - strcpy(th->status, "RUNNING"); + th->doing = PTRACE_SINGLESTEP; + th->donext = 0; th->clearstates = 0; return 0; } @@ -592,7 +575,6 @@ int dbg_stepover(struct thread *th) { cs_disasm_iter(handle, &code, &size, &address, insn); - int ret = -1; if (insn->id == X86_INS_CALL) { struct breakpoint *b = add_breakpoint(th->proc, address); b->user = 0; @@ -600,14 +582,18 @@ int dbg_stepover(struct thread *th) { b->tid = th->id; b->enabled = -1; - ret = dbg_cont(th); + th->doing = PTRACE_SINGLESTEP; + th->donext = PTRACE_CONT; + th->clearstates = 0; } else { - ret = dbg_stepin(th); + th->doing = PTRACE_SINGLESTEP; + th->donext = 0; + th->clearstates = 0; } cs_free(insn, 1); cs_close(&handle); - return ret; + return 0; } int dbg_stepback(struct thread *th) { @@ -52,8 +52,8 @@ struct thread { pid_t id; int stopped; int signal; - int cont; - int shouldcont; + int doing; + int donext; char status[128]; }; @@ -65,7 +65,7 @@ extern struct process *dbg_attach(pid_t pid, int child); extern void dbg_detach(struct process *proc); extern int dbg_free(struct thread *th); -extern int dbg_wait(struct thread *th, int primary); +extern void dbg_sync(struct process *proc); extern int dbg_intr(struct thread *th); extern int dbg_cont(struct thread *th); @@ -149,10 +149,7 @@ static void info_update(struct thread *th, PANEL *pan) { static void wait_all_threads(struct list *processes) { for (struct process *proc = processes->head; proc != processes->end; proc = proc->next) { - struct list *threads = &proc->threads; - for (struct thread *th = threads->head; th != threads->end; th = th->next) { - dbg_wait(th, 0); - } + dbg_sync(proc); } } |