Make strace correctly handle SIGTRAP produced by e.g.
Andreas Schwab
schwab at redhat.com
Mon Nov 2 12:55:33 UTC 2009
>From 15a8755d7a53dbd035b914b8fb0eeafe6ba9dcd2 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <dvlasenk at redhat.com>
Date: Mon, 22 Dec 2008 19:14:47 +0000
Subject: [PATCH] Make strace correctly handle SIGTRAP produced by e.g.
kill(2) and by trapping instruction.
* defs.h: Add sigtrap80 field to struct tcb.
* strace.c (alloc_tcb): Initialize it to SIGTRAP.
(detach): Use tcp->sigtrap80 instead of SIGTRAP constant.
(trace): Attempt to set PTRACE_O_TRACESYSGOOD and
PTRACE_O_TRACEEXEC options on each newly attached process,
distinquish between SIGTRAP and (SIGTRAP | 0x80) stops.
Fixes RH#162774 "strace ignores int3 SIGTRAP".
---
defs.h | 4 ++-
strace.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h
index 5bcaa07..1694e0b 100644
--- a/defs.h
+++ b/defs.h
@@ -335,8 +335,10 @@ struct tcb {
int nclone_threads; /* # of nchildren with CLONE_THREAD */
int nclone_detached; /* # of nchildren with CLONE_DETACHED */
int nclone_waiting; /* clone threads in wait4 (TCB_SUSPENDED) */
-#endif
/* (1st arg of wait4()) */
+#endif
+ int sigtrap80; /* What sig we consider to be ptrace stop */
+ /* (can be SIGTRAP or (SIGTRAP|0x80) only) */
long baddr; /* `Breakpoint' address */
long inst[2]; /* Instructions on above */
int pfd; /* proc file descriptor */
diff --git a/strace.c b/strace.c
index 7294e8e..ecbac78 100644
--- a/strace.c
+++ b/strace.c
@@ -1016,6 +1016,7 @@ alloc_tcb(int pid, int command_options_parsed)
tcp->nclone_waiting = 0;
#endif
tcp->flags = TCB_INUSE | TCB_STARTUP;
+ tcp->sigtrap80 = SIGTRAP;
tcp->outf = outf; /* Initialise to current out file */
tcp->curcol = 0;
tcp->stime.tv_sec = 0;
@@ -1622,7 +1623,7 @@ int sig;
break;
}
error = ptrace_restart(PTRACE_CONT, tcp,
- WSTOPSIG(status) == SIGTRAP ? 0
+ WSTOPSIG(status) == tcp->sigtrap80 ? 0
: WSTOPSIG(status));
if (error < 0)
break;
@@ -2484,10 +2485,77 @@ Process %d attached (waiting for parent)\n",
return -1;
}
}
+#ifdef LINUX /* add more OSes after you verified it works for them */
+ /*
+ * Ask kernel to set signo to SIGTRAP | 0x80
+ * on ptrace-generated SIGTRAPs, and mark
+ * execve's SIGTRAP with PTRACE_EVENT_EXEC.
+ */
+ if (tcp->sigtrap80 == SIGTRAP
+ && ptrace(PTRACE_SETOPTIONS, pid, (char *) 0,
+ (void *) (PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC)) == 0) {
+ tcp->sigtrap80 = SIGTRAP | 0x80;
+ }
+#endif
goto tracing;
}
- if (WSTOPSIG(status) != SIGTRAP) {
+#ifdef LINUX
+ if (tcp->sigtrap80 != SIGTRAP && WSTOPSIG(status) == SIGTRAP) {
+ /*
+ * We told ptrace to report SIGTRAP | 0x80 on this process
+ * but got bare SIGTRAP. This can be a genuine SIGTRAP:
+ * kill(pid, SIGTRAP), trap insn, etc;
+ * but be paranoid about it.
+ */
+ if (((unsigned)status >> 16) == PTRACE_EVENT_EXEC) {
+ /* It's post-exec ptrace stop. */
+ /* Set WSTOPSIG(status) = (SIGTRAP | 0x80). */
+ status |= 0x8000;
+ } else {
+ /* Take a better look... */
+ siginfo_t si;
+ ptrace(PTRACE_GETSIGINFO, pid, (void*) 0, (void*) &si);
+ /*
+ * Check some fields to make sure we see
+ * real SIGTRAP.
+ * Otherwise interpret it as ptrace stop.
+ * Real SIGTRAPs (int3 insn on x86, kill() etc)
+ * have these values:
+ * int3: kill -TRAP $pid:
+ * si_signo:5 (SIGTRAP) si_signo:5 (SIGTRAP)
+ * si_errno:0 si_errno:(?)
+ * si_code:128 (SI_KERNEL) si_code:0 (SI_USER)
+ * si_pid:0 si_pid:(>0?)
+ * si_band:0 si_band:(?)
+ * Ptrace stops have garbage there instead.
+ */
+ if (si.si_signo != SIGTRAP
+ || (si.si_code != SI_KERNEL && si.si_code != SI_USER)
+ ) {
+ fprintf(stderr, "bogus SIGTRAP (si_code:%x), assuming it's ptrace stop\n", si.si_code);
+ /* Set WSTOPSIG(status) = (SIGTRAP | 0x80). */
+ status |= 0x8000;
+ }
+ }
+ }
+
+ if (WSTOPSIG(status) == (SIGTRAP | 0x80)
+ /* && tcp->sigtrap80 == SIGTRAP - redundant */
+ ) {
+ /*
+ * If tcp->sigtrap80 == SIGTRAP but we got it
+ * ORed with 0x80, it's a CLONE_PTRACEd child
+ * which inherited "SIGTRAP | 0x80" setting.
+ * Whee. Just record this remarkable fact.
+ */
+ tcp->sigtrap80 = (SIGTRAP | 0x80);
+ }
+#endif
+
+ if (WSTOPSIG(status) != tcp->sigtrap80) {
+ /* This isn't a ptrace stop. */
+
if (WSTOPSIG(status) == SIGSTOP &&
(tcp->flags & TCB_SIGTRAPPED)) {
/*
--
1.6.5.1
Andreas.
--
Andreas Schwab, schwab at redhat.com
GPG Key fingerprint = D4E8 DBE3 3813 BB5D FA84 5EC7 45C6 250E 6F00 984E
"And now for something completely different."
More information about the Strace-devel
mailing list