]> granicus.if.org Git - strace/commitdiff
Make strace correctly handle SIGTRAP produced by e.g.
authorDenys Vlasenko <dvlasenk@redhat.com>
Mon, 22 Dec 2008 19:14:47 +0000 (19:14 +0000)
committerDenys Vlasenko <dvlasenk@redhat.com>
Mon, 22 Dec 2008 19:14:47 +0000 (19:14 +0000)
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
defs.h
strace.c

index e181e68e85f2710ded378bd818e473d99bca3848..ee84b498cd52d839dbde50e67db66dcd61099843 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2008-12-22  Denys Vlasenko  <dvlasenk@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@redhat.com>
 
        Make strace detect when traced process suddenly disappeared
diff --git a/defs.h b/defs.h
index f943ea95aae78f1f2dd59df2e666d9153fc602f0..f1c52f495a0acf0827ce468c5302dec0b62e5a49 100644 (file)
--- 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 */
index 13459d8772c4a1115171ae2c2e3c4a2d1b0cb6bc..5f5076077317b2dd665e051335513cb617c76297 100644 (file)
--- 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)) {
                                /*