Fixed segfault in traced process when receiving a signal
Specifically, when a signal was received while enabling a breakpoint
diff --git a/ChangeLog b/ChangeLog
index 7a09e57..41c496a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2009-02-11 Juan Cespedes <cespedes@debian.org>
+
+ * Fixed bug present since the first version (!) of ltrace,
+ which caused many programs to segfault when a signal is received:
+ + When a breakpoint is hit, and we need to continue with it, we:
+ 1) remove the breakpoint
+ 2) order a SINGLESTEP
+ 3) when control comes back, set the breakpoint again
+ 4) let the process continue
+ + The problem came when a signal is received in the middle
+ of all this (specifically, between 2) and 3)).
+ If this is so, we treat the signal "in the usual way",
+ it is, at the end we issue a "CONTINUE" instead of the
+ needed SINGLESTEP.
+
2008-12-10 Juan Cespedes <cespedes@debian.org>
* summary.c: Fix "ltrace -o -c"
diff --git a/ltrace.h b/ltrace.h
index f029664..fd60594 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -216,6 +216,7 @@
extern void *instruction_pointer;
extern struct event *wait_for_something(void);
+extern struct process * pid2proc(pid_t pid);
extern void process_event(struct event *event);
extern void execute_program(struct process *, char **);
extern int display_arg(enum tof type, struct process *proc, int arg_num, arg_type_info *info);
diff --git a/proc.c b/proc.c
index 511eb5e..88edd0a 100644
--- a/proc.c
+++ b/proc.c
@@ -59,3 +59,17 @@
continue_process(pid);
proc->breakpoints_enabled = 1;
}
+
+struct process *
+pid2proc(pid_t pid) {
+ struct process *tmp;
+
+ tmp = list_of_processes;
+ while (tmp) {
+ if (pid == tmp->pid) {
+ return tmp;
+ }
+ tmp = tmp->next;
+ }
+ return NULL;
+}
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index f348fc8..a90d46a 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -197,13 +197,25 @@
void
continue_after_signal(pid_t pid, int signum) {
- /* We should always trace syscalls to be able to control fork(), clone(), execve()... */
- ptrace(PTRACE_SYSCALL, pid, 0, signum);
+ struct process *proc;
+
+ proc = pid2proc(pid);
+ if (proc && proc->breakpoint_being_enabled) {
+#if defined __sparc__ || defined __ia64___
+ ptrace(PTRACE_SYSCALL, pid, 0, signum);
+#else
+ ptrace(PTRACE_SINGLESTEP, pid, 0, signum);
+#endif
+ } else {
+ ptrace(PTRACE_SYSCALL, pid, 0, signum);
+ }
}
void
continue_process(pid_t pid) {
- continue_after_signal(pid, 0);
+ /* We always trace syscalls to control fork(), clone(), execve()... */
+
+ ptrace(PTRACE_SYSCALL, pid, 0, 0);
}
void
diff --git a/wait_for_something.c b/wait_for_something.c
index 0f57fe5..eb7ea52 100644
--- a/wait_for_something.c
+++ b/wait_for_something.c
@@ -16,10 +16,6 @@
static struct event event;
-/* This should also update `current_process' */
-
-static struct process *pid2proc(int pid);
-
struct event *
wait_for_something(void) {
pid_t pid;
@@ -154,17 +150,3 @@
event.proc->instruction_pointer - DECR_PC_AFTER_BREAK;
return &event;
}
-
-static struct process *
-pid2proc(pid_t pid) {
- struct process *tmp;
-
- tmp = list_of_processes;
- while (tmp) {
- if (pid == tmp->pid) {
- return tmp;
- }
- tmp = tmp->next;
- }
- return NULL;
-}