aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/_posts/2022-11-07-ptrace-sigtraps.md
blob: 2f42a81bcd1c35f828d40cfee127baf09c19cfe9 (plain) (tree)
1
2
3
4
5
6
7
8





                                                                             

                                                                              































































                                                                                 
---
title: 'Fun with ptrace: SIGTRAPs galore'
date: 2022-11-07 13:00 +0100
---
When using `PTRACE_ATTACH` the `ptrace` mechanism reuses SIGTRAP for a number
of things by default.
This makes it unnecessarily hard to distinguish regular traps (possibly caused
by breakpoints we might place) from other events.

1. After `ptrace(PTRACE_SYSCALL)`, syscall-stops will be reported as SIGTRAPs.

   ```c
   int status;

   ptrace(PTRACE_SYSCALL, pid, 0, 0);
   waitpid(pid, &status, 0);

   if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
       /* We don't know if the tracee has just entered/exited a syscall or
        * received a regular SIGTRAP (could be caused by a breakpoint we
        * placed). */
   }
   ```

   This is fixed by using the `PTRACE_O_TRACESYSGOOD` option.

   ```c
   int status;

   ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESYSGOOD);
   ptrace(PTRACE_SYSCALL, pid, 0, 0);
   waitpid(pid, &status, 0);

   if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) {
       /* We know for sure that the tracee has just entered/exited a
        * syscall. */
   }
   ```

2. Every `execve` call will be reported as a SIGTRAP.

   ```c
   int status;

   ptrace(PTRACE_CONT, pid, 0, 0);
   waitpid(pid, &status, 0);

   if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
       /* We don't know if the tracee just called execve() or received a
        * regular SIGTRAP (could be caused by a breakpoint we placed). */
   }
   ```

   This is fixed by using the `PTRACE_O_TRACEEXEC` option.

   ```c
   int status;

   ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEEXEC);
   ptrace(PTRACE_CONT, pid, 0, 0);
   waitpid(pid, &status, 0);

   if (WIFSTOPPED(status) && status >> 8 == (SIGTRAP | PTRACE_EVENT_EXEC << 8)) {
       /* We know for sure that the tracee has just called execve(). */
   }
   ```

   This point doesn't apply to tracees attached using `PTRACE_SEIZE`.
   {: .alert .alert-info }

As you can see, you should always use at least the `PTRACE_O_TRACESYSGOOD` and
`PTRACE_O_TRACEEXEC` options to be able to distinguish between SIGTRAPs.