From 80a43bdbdeee87605fdbcdd79195edc98b98fd1d Mon Sep 17 00:00:00 2001 From: Malfurious Date: Mon, 9 Oct 2023 13:54:40 -0400 Subject: Detect out-of-band thread exec state changes to prevent deadlock Signed-off-by: Malfurious --- debugger.c | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'debugger.c') diff --git a/debugger.c b/debugger.c index 3435cf4..5981660 100644 --- a/debugger.c +++ b/debugger.c @@ -192,13 +192,37 @@ static void capture_state(struct process *proc) { } } +static char thread_state(struct thread *th) { + char statpath[32], stat[512]; + snprintf(statpath, sizeof(statpath), "/proc/%li/stat", (long)th->id); + + FILE *f = fopen(statpath, "r"); + if (f) { + fread(stat, 1, sizeof(stat), f); + fclose(f); + + char *c = strchr(stat, ' '); + c = strchr(c+1, ' ') + 1; + return *c; + } + + return 0; +} + 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 (!wait_thread(th)) {} + + char state; + do { + state = thread_state(th); + } while (state != 't' && state != 'D'); + + wait_thread(th); + //if (STOP_ALL_ON_EVENT) { // th->cont = 0; // th->shouldcont = 0; @@ -211,13 +235,20 @@ static void continue_all_threads(struct process *proc) { struct list *threads = &proc->threads; for (struct thread *th = threads->head; th != threads->end; th = th->next) { if (th->id > 0) { - ptrace(PTRACE_CONT, th->id, NULL, th->signal); - th->stopped = 0; - th->signal = 0; - th->donext = 0; - th->doing = PTRACE_CONT; - strcpy(th->status, "RUNNING"); - th->clearstates = 1; + char state; + do { + wait_thread(th); + + ptrace(PTRACE_CONT, th->id, NULL, th->signal); + th->stopped = 0; + th->signal = 0; + th->donext = 0; + th->doing = PTRACE_CONT; + strcpy(th->status, "RUNNING"); + th->clearstates = 1; + + state = thread_state(th); + } while (state == 't'); } } } @@ -435,12 +466,10 @@ void dbg_detach(struct process *proc) { /* ptrace requires that threads be stopped before calling PTRACE_DETACH. * In some cases, a blocked thread can possibly have a pending trap which * would crash the process if encountered after ptracing. We cycle an extra - * continue/interrupt to consume this trap event ourselves before detaching. - * Threads must actually get scheduled for this to be effective, which is - * the reason for the usleep. A better approach is welcome here. */ + * continue/interrupt to consume this trap event ourselves before + * detaching. */ interrupt_all_threads(proc); continue_all_threads(proc); - usleep(SCHEDULER_DELAY); interrupt_all_threads(proc); /* Supplement to PTRACE_O_EXITKILL */ -- cgit v1.2.3