Fix a race between waiting for SIGSTOP delivery and task exit
diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c
index e854e66..9b05ce2 100644
--- a/sysdeps/linux-gnu/proc.c
+++ b/sysdeps/linux-gnu/proc.c
@@ -1,3 +1,4 @@
+#define _GNU_SOURCE /* For getline. */
#include "config.h"
#include "common.h"
@@ -14,6 +15,7 @@
#include <ctype.h>
#include <errno.h>
#include <sys/syscall.h>
+#include <error.h>
/* /proc/pid doesn't exist just after the fork, and sometimes `ltrace'
@@ -174,10 +176,13 @@
if (file != NULL) {
each_line_starting(file, "State:\t", &process_status_cb, &ret);
fclose(file);
- }
- if (ret == ps_invalid)
- fprintf(stderr, "Couldn't determine status of process %d\n",
- pid);
+ if (ret == ps_invalid)
+ error(0, errno, "process_status %d", pid);
+ } else
+ /* If the file is not present, the process presumably
+ * exited already. */
+ ret = ps_zombie;
+
return ret;
}
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 8efd284..6e17850 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -164,9 +164,11 @@
/**
* This is used for bookkeeping related to PIDs that the event
- * handlers work with. */
+ * handlers work with.
+ */
struct pid_task {
- pid_t pid;
+ pid_t pid; /* This may be 0 for tasks that exited
+ * mid-handling. */
int sigstopped;
int got_event;
int delivered;
@@ -231,6 +233,7 @@
static struct pid_task *
get_task_info(struct pid_set * pids, pid_t pid)
{
+ assert(pid != 0);
size_t i;
for (i = 0; i < pids->count; ++i)
if (pids->tasks[i].pid == pid)
@@ -307,7 +310,8 @@
self->task_enabling_breakpoint->pid);
size_t i;
for (i = 0; i < self->pids.count; ++i)
- if (self->pids.tasks[i].delivered)
+ if (self->pids.tasks[i].pid != 0
+ && self->pids.tasks[i].delivered)
continue_process(self->pids.tasks[i].pid);
continue_process(self->task_enabling_breakpoint->pid);
destroy_event_handler(leader);
@@ -349,7 +353,8 @@
{
size_t i;
for (i = 0; i < pids->count; ++i) {
- if (pids->tasks[i].sigstopped
+ if (pids->tasks[i].pid != 0
+ && pids->tasks[i].sigstopped
&& !pids->tasks[i].delivered
&& pids->tasks[i].got_event) {
debug(DEBUG_PROCESS, "continue %d for SIGSTOP delivery",
@@ -360,11 +365,16 @@
}
static int
+event_exit_p(Event * event)
+{
+ return event != NULL && (event->type == EVENT_EXIT
+ || event->type == EVENT_EXIT_SIGNAL);
+}
+
+static int
event_exit_or_none_p(Event * event)
{
- return event == NULL
- || event->type == EVENT_EXIT
- || event->type == EVENT_EXIT_SIGNAL
+ return event == NULL || event_exit_p(event)
|| event->type == EVENT_NONE;
}
@@ -393,7 +403,8 @@
int all_clear = 1;
size_t i;
for (i = 0; i < pids->count; ++i)
- if (pids->tasks[i].sigstopped
+ if (pids->tasks[i].pid != 0
+ && pids->tasks[i].sigstopped
&& !pids->tasks[i].delivered) {
all_clear = 0;
break;
@@ -435,8 +446,9 @@
if (each_task(leader, &task_stopped, NULL) == NULL) {
debug(DEBUG_PROCESS, "all stopped, now SINGLESTEP %d",
self->task_enabling_breakpoint->pid);
- ptrace(PTRACE_SINGLESTEP,
- self->task_enabling_breakpoint->pid, 0, 0);
+ if (ptrace(PTRACE_SINGLESTEP,
+ self->task_enabling_breakpoint->pid, 0, 0))
+ perror("PTRACE_SINGLESTEP");
self->state = state = psh_singlestep;
}
break;
@@ -470,6 +482,9 @@
process_stopping_done(self, leader);
}
+ if (event_exit_p(event) && task_info != NULL)
+ task_info->pid = 0;
+
if (event != NULL && event_to_queue) {
enque_event(event);
event = NULL; // sink the event
@@ -648,6 +663,9 @@
size_t i;
for (i = 0; i < other->pids.count; ++i) {
struct pid_task * oti = &other->pids.tasks[i];
+ if (oti->pid == 0)
+ continue;
+
struct pid_task * task_info
= add_task_info(&handler->pids, oti->pid);
if (task_info == NULL) {