Skip to content
/linux-syscalls

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

ArchitectureNumberABIEntry point
x86 (i386)26i386sys_ptrace
x64 (x86_64)10164sys_ptrace
ARM64 (aarch64)117

Kernel history

Introduced in Linux 1.0.

  1. 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. 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. 3.4

    PTRACE_SEIZE was introduced to attach without stopping the tracee — required for race-free tracer attachment and used by modern debuggers.

  4. 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}]) = 0

Tracing 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

Related syscalls