[RFC] make strace handle SIGTRAP properly

Tejun Heo tj at kernel.org
Fri May 20 14:18:19 UTC 2011

Hello, Denys.

On Fri, May 20, 2011 at 02:08:03PM +0200, Denys Vlasenko wrote:
> During recent lkml discussions about fixing some long-standing problems
> with ptrace, I had to look in strace source and experiment with it a
> bit. (CC-ing some participants).
> One irritating thing I noticed is that we *still* don't handle
> user-generated SIGTRAPs. There are users who do want that to work:
> https://bugzilla.redhat.com/show_bug.cgi?id=162774
> Currently, strace "handles" SIGTRAP by code like this:
> #if defined (I386)
>         if (upeek(tcp, 4*EAX, &eax) < 0)
>                 return -1;
>         if (eax != -ENOSYS && !(tcp->flags & TCB_INSYSCALL)) {
>                 if (debug)
>                         fprintf(stderr, "stray syscall exit: eax = %ld\n", eax);
>                 return 0;
>         }
> #elif ...

That is truly scary; however, it doesn't have to be this scary even
without TRACESYSGOOD.  Again, the documentation is too light on
details.  I'm planning on writing all this up but here's a brief

Primarily, what's being reported to the ptracer via wait(2) (and
SIGCHLD) are traps that the tracee are taking.  Entering trap means
that tracee leaves RUNNING state and enters TRACED state.  When in
this state, tracee won't resume execution until directed so by ptracer

There are different trap sites inside the kernel, which appear as
different trap types to the ptracer.  Most trap sites are there to
report certain events - tracee is about to do something or finished
something kind of things.  Other than continuing (and possibly
injecting signal via @data depending on trap site, but please don't do
this), there isn't whole lot ptracer wants to do with these traps

However, two are somewhat special.  The first one is signal delivery
trap.  This trap site sits in the signal delivery path (of course) and
gets triggered right after the signal is dequeued from pending queue
but before actually being delievered.  The ptracer can change the
siginfo and signo or even squash the signla altogether.

The second is group stop trap.  When a stop signal is received, the
whole process (task group) enters group stop.  IOW, delivery of a stop
signal by any task in a process initiates group stop and generation
(not delivery, so the action of sending signal itself) of SIGCONT ends
it.  Once group stop is initiated, each task in the process
participates in the group stop by stopping if not ptraced and by
trapping at group stop trap site if ptraced.[1]

So, when a trap is reported via wait(2), the first thing to do is
determining which trap tracee has taken, which should have been easy
and apparent but unfortunately a bit convoluted and undocumented, but
it's doable.

The following exit_code is used.

 * The signal being delivered for signal delivery trap.

 * The signal number which initiated the group stop for group stop

 * SIGTRAP | optional PTRACE_EVENT_* << 8 for other traps.

However, it's immediately apparent that exit_code itself isn't
sufficient in determining the specific trap site taken.
PTRACE_GETSIGINFO can shed some light.

 * On signal delivery, it contains the siginfo of the signal.  In this
   case, si_code always contains either 0 or negative number.  It
   can't contain a positive number no matter how the signal is

 * On group stop, there is no siginfo and PTRACE_GETSIGINFO will fail
   with -EINVAL.

 * On other traps, si_code equals exit_code - SIGTRAP | optional
   PTRACE_EVENT_* << 8.

So, here are the steps a program can take to determine the trap type.

 1. Test whether exit_code is SIGTRAP | PTRACE_EVENT_* << 8.  If so,
    it's one of PTRACE_EVENT traps (let's include TRACESYSGOOD in this
    category too).

 2. Otherwise, execute PTRACE_GETSIGINFO.  If the returned si_code is
    0 or negative, signal is being delivered.  If there's no siginfo,
    tracee is participating in a group stop.  If si_code equals
    exit_code (it would have to be SIGTRAP), it stopped for a ptrace
    trap without PTRACE_EVENT_* code (!TRACESYSGOOD).

I might have missed something but the above should be able to serve as
starting point at the very least.

Thank you.

PS. It will be eaiser for PTRACE_SEIZE.  GETSIGINFO would be required
    only when exit_code is SIGTRAP | PTRACE_EVENT_STOP.

[1] Unfortunately, group stop behavior while ptraced is largely broken
    in pre-2.6.40 kernels.  :-(


More information about the Strace-devel mailing list