[PATCH] Try to use PTRACE_SETOPTIONS to handle non-ptrace SIGTRAP

Andreas Schwab schwab at redhat.com
Thu Mar 4 14:17:47 UTC 2010


* defs.h (TCB_PTRACE_OPTIONS) [LINUX && PTRACE_SETOPTIONS]:
Define.
* process.c (internal_fork): Copy TCB_PTRACE_OPTIONS flag to
child.
(internal_exec): Only set TCB_WAITEXECVE on entering.
* strace.c (set_ptrace_options, is_ptrace_stop)
(is_post_execve_trap): Define.
(proc_open): Use them.
---
 defs.h    |   12 +++++++++
 process.c |   10 +++++--
 strace.c  |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 97 insertions(+), 4 deletions(-)

diff --git a/defs.h b/defs.h
index 4797e96..9001a85 100644
--- a/defs.h
+++ b/defs.h
@@ -378,6 +378,18 @@ struct tcb {
 # define TCB_CLONE_DETACHED 04000 /* CLONE_DETACHED set in creating syscall */
 # define TCB_CLONE_THREAD  010000 /* CLONE_THREAD set in creating syscall */
 # define TCB_GROUP_EXITING 020000 /* TCB_EXITING was exit_group, not _exit */
+# if defined LINUX && (defined PTRACE_SETOPTIONS || defined PT_SETOPTIONS)
+#  ifndef PTRACE_O_TRACESYSGOOD
+#   define PTRACE_O_TRACESYSGOOD 0x00000001
+#  endif
+#  ifndef PTRACE_O_TRACEEXEC
+#   define PTRACE_O_TRACEEXEC    0x00000010
+#  endif
+#  ifndef PTRACE_EVENT_EXEC
+#   define PTRACE_EVENT_EXEC     4
+#  endif
+#  define TCB_PTRACE_OPTIONS 040000 /* PTRACE_SETOPTIONS called */
+# endif
 # include <sys/syscall.h>
 # ifndef __NR_exit_group
 # /* Hack: Most headers around are too old to have __NR_exit_group.  */
diff --git a/process.c b/process.c
index c69e45e..cc02921 100644
--- a/process.c
+++ b/process.c
@@ -866,6 +866,9 @@ internal_fork(struct tcb *tcp)
 				clearbpt(tcpchild);
 
 			tcpchild->flags &= ~(TCB_SUSPENDED|TCB_STARTUP);
+#ifdef TCB_PTRACE_OPTIONS
+			tcpchild->flags |= tcp->flags & TCB_PTRACE_OPTIONS;
+#endif
 			if (ptrace_restart(PTRACE_SYSCALL, tcpchild, 0) < 0)
 				return -1;
 
@@ -1732,9 +1735,10 @@ struct tcb *tcp;
 		fixvfork(tcp);
 #endif /* SUNOS4 */
 #if defined LINUX && defined TCB_WAITEXECVE
-	if (exiting(tcp) && syserror(tcp))
-		tcp->flags &= ~TCB_WAITEXECVE;
-	else
+	if (exiting(tcp)) {
+		if (syserror(tcp))
+			tcp->flags &= ~TCB_WAITEXECVE;
+	} else
 		tcp->flags |= TCB_WAITEXECVE;
 #endif /* LINUX && TCB_WAITEXECVE */
 	return 0;
diff --git a/strace.c b/strace.c
index 2fb75c9..701d366 100644
--- a/strace.c
+++ b/strace.c
@@ -2253,6 +2253,75 @@ handle_group_exit(struct tcb *tcp, int sig)
 }
 #endif
 
+#ifdef TCB_PTRACE_OPTIONS
+
+static int use_ptrace_setoptions = 1;
+static int probe_ptrace_setoptions = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
+
+static void
+set_ptrace_options(struct tcb *tcp)
+{
+	if (!use_ptrace_setoptions)
+		return;
+
+	/*
+	 * Ask kernel to set signo to SIGTRAP | 0x80
+	 * on ptrace-generated SIGTRAPs, and mark
+	 * execve's SIGTRAP with PTRACE_EVENT_EXEC.
+	 */
+	if (ptrace(PTRACE_SETOPTIONS, tcp->pid, (char *) 0,
+		   PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC) == 0)
+		tcp->flags |= TCB_PTRACE_OPTIONS;
+	else if (errno != ESRCH)
+		use_ptrace_setoptions = 0;
+}
+
+static int
+is_ptrace_stop(struct tcb *tcp, int status)
+{
+	if (!(tcp->flags & TCB_PTRACE_OPTIONS))
+		return WSTOPSIG(status) == SIGTRAP;
+
+	if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
+		/* PTRACE_O_TRACESYSGOOD works. */
+		probe_ptrace_setoptions &= PTRACE_O_TRACESYSGOOD;
+		return 1;
+	}
+	if (WSTOPSIG(status) == SIGTRAP &&
+	    ((unsigned)status >> 16) == PTRACE_EVENT_EXEC) {
+		/* PTRACE_O_TRACEEXEC works. */
+		probe_ptrace_setoptions &= PTRACE_O_TRACEEXEC;
+		return 1;
+	}
+
+	if ((WSTOPSIG(status) == SIGTRAP && probe_ptrace_setoptions))
+		return 1;
+	return 0;
+}
+
+static int
+is_post_execve_trap(struct tcb *tcp, int status)
+{
+	if (!use_ptrace_setoptions)
+		return 0;
+	
+	if (WSTOPSIG(status) == SIGTRAP) {
+		if (((unsigned)status >> 16) == PTRACE_EVENT_EXEC)
+			return 1;
+		/* A SIGTRAP received before the first ptrace stop
+		   after attaching must be the post-execve trap.  */
+		if ((tcp->flags & (TCB_STARTUP|TCB_ATTACHED|TCB_PTRACE_OPTIONS)) ==
+		    (TCB_STARTUP|TCB_ATTACHED))
+			return 1;
+	}
+	return 0;
+}
+#else
+#define set_ptrace_options(tcp)
+#define is_ptrace_stop(tcp, status) (WSTOPSIG(status) == SIGTRAP)
+#define is_post_execve_trap(tcp, status) 0
+#endif
+
 static int
 trace()
 {
@@ -2465,10 +2534,11 @@ Process %d attached (waiting for parent)\n",
 					return -1;
 				}
 			}
+			set_ptrace_options(tcp);
 			goto tracing;
 		}
 
-		if (WSTOPSIG(status) != SIGTRAP) {
+		if (!is_ptrace_stop(tcp, status)) {
 			if (WSTOPSIG(status) == SIGSTOP &&
 					(tcp->flags & TCB_SIGTRAPPED)) {
 				/*
@@ -2533,6 +2603,13 @@ Process %d attached (waiting for parent)\n",
 		/* we handled the STATUS, we are permitted to interrupt now. */
 		if (interrupted)
 			return 0;
+		/* Ignore post-execve trap. */
+		if (is_post_execve_trap(tcp, status)) {
+#ifdef TCB_WAITEXECVE
+			tcp->flags &= ~TCB_WAITEXECVE;
+#endif
+			goto tracing;
+		}
 		if (trace_syscall(tcp) < 0 && !tcp->ptrace_errno) {
 			/* ptrace() failed in trace_syscall() with ESRCH.
 			 * Likely a result of process disappearing mid-flight.
-- 
1.7.0.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