Debug & Trace · Section 2
ptrace(2)
Inspect and control another process — the kernel primitive behind gdb, strace, and process-injection malware.
Signature
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void * addr, void * data);- request
- Operation to perform. See ptrace(2) for the full list (40+ requests on modern kernels).
- pid
- Process ID of the tracee. Ignored for PTRACE_TRACEME (which operates on the caller).
- addr
- Operation-specific address argument (e.g. memory location to peek/poke).
- data
- Operation-specific data argument (value to write, pointer to a struct, signal number, etc.).
Description
ptrace() lets the tracer process observe and modify the execution of a tracee — reading or writing the tracee's memory and registers, intercepting its signals, stopping it before or after each syscall, and resuming it under controlled conditions. The request argument selects the operation (e.g. PTRACE_ATTACH, PTRACE_PEEKDATA, PTRACE_SYSCALL); pid identifies the tracee; addr and data carry operation-specific arguments. ptrace() is privileged: the tracer must either be ptracing itself (PTRACE_TRACEME), have CAP_SYS_PTRACE in the tracee's user namespace, or pass the Yama ptrace_scope check (default 1: only ancestor processes may attach). It is the foundation of every debugger and tracer on Linux.
Architecture mapping
| Architecture | Number | ABI | Entry point |
|---|---|---|---|
| x86 (i386) | 26 | i386 | sys_ptrace |
| x64 (x86_64) | 101 | 64 | sys_ptrace |
| ARM64 (aarch64) | 117 | — | — |
Kernel history
Introduced in Linux 1.0.
1.0
ptrace() has been part of Linux since 1.0, with semantics broadly inherited from BSD/SysV but heavily extended over the decades.
2.6.34
The Yama LSM was added to constrain ptrace by relationship: kernel.yama.ptrace_scope sets how processes may attach (0 = classic, 1 = ancestor-only, 2 = admin-only, 3 = disabled). Default 1 on Ubuntu, Debian, Fedora.
3.4
PTRACE_SEIZE was introduced to attach without stopping the tracee — required for race-free tracer attachment and used by modern debuggers.
4.10
ptrace interactions with execve of set-UID and AT_SECURE binaries were hardened to prevent privilege-escalation tricks where a tracer modifies the binary during the brief load window.
seccomp & containers
Docker default profile
Blocked
Podman default profile
Blocked
ptrace() is BLOCKED by default in Docker and Podman seccomp profiles. Allowing it inside a container effectively grants CAP_SYS_PTRACE within the container's user namespace and lets compromised workloads inject code into sibling processes. Enable it only for debugging containers (run with --cap-add=SYS_PTRACE --security-opt seccomp=unconfined, never in production). Inside a workload pod that has no legitimate need for a debugger, an attempt to call ptrace() is a strong incident signal.
libseccomp
// ptrace is denied by default in Docker/Podman; explicitly block it for safety
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(ptrace), 0);strace example
$ strace -e ptrace strace -e read true 2>&1 | head -5
ptrace(PTRACE_TRACEME) = 0
ptrace(PTRACE_SETOPTIONS, 14723, NULL, PTRACE_O_TRACESYSGOOD|PTRACE_O_TRACEEXEC|PTRACE_O_TRACEEXIT) = 0
ptrace(PTRACE_SYSCALL, 14723, NULL, 0) = 0
ptrace(PTRACE_GETREGSET, 14723, NT_PRSTATUS, [{iov_base=…, iov_len=216}]) = 0Tracing strace tracing another command demonstrates the ptrace loop directly — every PTRACE_SYSCALL stop becomes a printed syscall line. Use -e trace=ptrace to filter when investigating a suspect process. Note that two strace instances cannot trace the same target (EBUSY).
Security & observability
ptrace is the canonical Linux process-injection primitive used by red teams and APTs alike: PTRACE_ATTACH a sibling, PTRACE_POKEDATA shellcode into its address space, set the instruction pointer with PTRACE_SETREGS, PTRACE_DETACH. The eBPF tracepoint sys_enter_ptrace fires for every call; pair with /proc/<pid>/status TracerPid for a complete view. Yama's ptrace_scope is the cheap-and-effective mitigation: set it to 2 (admin-only) on production hosts unless you have a specific need otherwise. On systemd hosts, ProtectKernelModules and SystemCallFilter at the unit level can further constrain debugging surface.
Errors
- EBUSY
- The target is already being traced by another tracer.
- EFAULT
- —
- EINVAL
- —
- EIO
- request is invalid, or addr / data is invalid for the requested operation.
- EPERM
- The tracer lacks permission to trace the target (Yama ptrace_scope, capability or namespace mismatch, or the target is set-UID without the tracer holding CAP_SYS_PTRACE).
- ESRCH
- No process with pid exists, or the target is not stopped when the operation requires it.
Flags
- PTRACE_TRACEME
- 0
- Indicate that this process is to be traced by its parent. Used by the tracee before exec.
- PTRACE_PEEKTEXT
- 1
- —
- PTRACE_PEEKDATA
- 2
- Read a word from the tracee's data segment at addr.
- PTRACE_POKETEXT
- 4
- —
- PTRACE_POKEDATA
- 5
- Write data to the tracee's data segment at addr — the primary process-injection primitive.
- PTRACE_CONT
- 7
- —
- PTRACE_KILL
- 8
- —
- PTRACE_SINGLESTEP
- 9
- —
- PTRACE_ATTACH
- 16
- Attach to an existing process and start tracing it. Sends SIGSTOP to the tracee.
- PTRACE_DETACH
- 17
- Stop tracing and resume the tracee. Equivalent to PTRACE_CONT followed by detach.
- PTRACE_SYSCALL
- 24
- Continue the tracee until the next syscall entry or exit — the building block of strace.
- PTRACE_SETOPTIONS
- 0x4200
- Configure tracer behaviour: PTRACE_O_TRACESYSGOOD, PTRACE_O_TRACEEXEC, PTRACE_O_EXITKILL, etc.
- PTRACE_GETSIGINFO
- 0x4202
- —
- PTRACE_SEIZE
- 0x4206
- Like ATTACH but does not stop the tracee; lets the tracer set options before any stops occur (added in 3.4).
- PTRACE_INTERRUPT
- 0x4207
- —