Make strace correctly handle SIGTRAP produced by e.g.

Andreas Schwab schwab at redhat.com
Mon Sep 7 12:13:17 UTC 2009


>From 1e3ce32a4fadb58230bca4f16baf8152579dd3d0 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".

---
 ChangeLog |   12 ++++++++++
 defs.h    |    4 ++-
 strace.c  |   72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 85 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e181e68..ee84b49 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2008-12-22  Denys Vlasenko  <dvlasenk at redhat.com>
+
+	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".
+
 2008-12-17  Denys Vlasenko  <dvlasenk at redhat.com>
 
 	Make strace detect when traced process suddenly disappeared
diff --git a/defs.h b/defs.h
index f943ea9..f1c52f4 100644
--- a/defs.h
+++ b/defs.h
@@ -322,8 +322,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 13459d8..5f50760 100644
--- a/strace.c
+++ b/strace.c
@@ -943,6 +943,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->stime.tv_sec = 0;
 			tcp->stime.tv_usec = 0;
@@ -1549,7 +1550,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;
@@ -2407,10 +2408,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.4


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