Ptrace
This document describes the process tracing (ptrace) interface in the Kyronix kernel. It is the child of Syscalls.
Syscall Table
- 101
ptrace- Process tracing and debugging
Operations
The ptrace syscall supports the following request codes:
PTRACE_TRACEME (0)
Sets the calling process’s tracer_pid to its parent PID. The process becomes traceable by its parent. No arguments are used.
PTRACE_ATTACH (16)
Attaches the calling process as a tracer to a target process:
- Look up the target process by PID via
proc_find(). - Verify the target is not already being traced (
tracer_pid == 0). If already traced, returnEPERM. - Set
t->tracer_pid = self->pid. - Send
SIGSTOPto the target process. - Return 0.
PTRACE_PEEKTEXT / PTRACE_PEEKDATA (1, 2)
Reads 8 bytes from the target process’s address space at address addr:
- Switch to the target’s address space via
vmm_switch(). - Validate the user pointer with
uptr_ok(). - Copy 8 bytes from the target address to a kernel buffer.
- Switch back to the caller’s address space.
- Write the result to the
datapointer in the caller’s address space.
Both request codes behave identically.
PTRACE_POKETEXT / PTRACE_POKEDATA (4, 5)
Writes 8 bytes to the target process’s address space at address addr:
- Switch to the target’s address space via
vmm_switch(). - Validate the user pointer with
uptr_ok_w(). - Copy 8 bytes from the
dataargument to the target address. - Switch back to the caller’s address space.
Both request codes behave identically.
PTRACE_GETREGS (12)
Reads the full register state of the target process into a ptrace_user_regs structure:
- Call
ptrace_fill_regs()to populate the structure from the target’s current frame. - Copy the structure to the caller’s
datapointer.
PTRACE_SETREGS (13)
Writes a ptrace_user_regs structure to the target process’s register state:
- Copy the
ptrace_user_regsstructure from the caller’sdatapointer. - Call
ptrace_store_regs()to apply the register values to the target’s current frame.
PTRACE_CONT (7)
Resumes the target process’s execution without syscall tracing or single-stepping:
- Verify the target is in a stopped state (
ptrace_stopped != 0). Otherwise, returnESRCH. - If a signal number is provided in
data, inject it as a pending signal. - Clear
ptrace_stoppedandptrace_reported. - Transition the target from
PROC_WAITINGtoPROC_READY.
PTRACE_SYSCALL (24)
Resumes the target process’s execution with syscall tracing enabled:
- Same as
PTRACE_CONT, but setsptrace_syscall_trace = 1. - The syscall dispatcher checks
ptrace_syscall_traceon entry and exit, stopping the process withSIGTRAP|0x80at each syscall boundary.
PTRACE_SINGLESTEP (9)
Resumes the target process’s execution with single-stepping enabled:
- Same as
PTRACE_CONT, but setsptrace_step = 1. - The target executes one instruction before being stopped again.
PTRACE_KILL (8)
Injects SIGKILL into the target process:
- Set the
SIGKILLbit in the target’spending_sigs. - Clear
ptrace_stoppedandptrace_reported. - Transition the target from
PROC_WAITINGtoPROC_READY.
PTRACE_DETACH (17)
Detaches the tracer from the target process:
- Clear
tracer_pidandptrace_syscall_trace. - If the target is stopped, clear
ptrace_stoppedand transition it toPROC_READY.
PTRACE_SETOPTIONS (0x4200)
Accepted as a no-op. Returns 0.
Register State
The ptrace_user_regs structure contains the full x86_64 general-purpose register state:
r15, r14, r13, r12, rbp, rbx, r11, r10, r9, r8,
rax, rcx, rdx, rsi, rdi, orig_rax, rip, cs, eflags,
rsp, ss, fs_base, gs_base, ds, es, fs, gs
Segment registers are set to kernel constants: cs = GDT_USER_CODE_SEL, ss/ds/es/fs/gs = GDT_USER_DATA_SEL. The fs_base is read from the target process’s fs_base field.
Frame Kinds
The ptrace_frame_kind field determines how register values are extracted and stored:
- Frame kind 1 (
syscall_frame_t) - Syscall entry frame.RIPis derived fromrcx(the syscall return address).RFLAGSis derived fromr11. This frame is used when the process is stopped at a syscall entry/exit viaSIGTRAP|0x80. - Frame kind 2 (
cpu_state_t) - Full interrupt frame.RIPandRFLAGSare read directly from the interrupt frame. This frame is used when the process is stopped via#BP(breakpoint) or#DB(debug) exceptions.
Permission Model
Only the tracer process (identified by t->tracer_pid == self->pid) may operate on a tracee. All operations except PTRACE_TRACEME and PTRACE_ATTACH verify the caller is the tracer. If the caller is not the tracer, the syscall returns ESRCH.
Exception Integration
The kernel’s Interrupt Descriptor Table (IDT) checks tracer_pid on #BP (vector 3) and #DB (vector 1) exceptions:
- On
#BP, the instruction pointer is decremented by 1 (past theint3opcode). - The process’s
ptrace_orig_raxis saved. proc_ptrace_stop()is called with frame kind 2, deliveringSIGTRAPto stop the process.
Syscall Dispatcher Integration
The syscall dispatcher checks ptrace_syscall_trace on every syscall entry and exit:
- On entry, if
ptrace_syscall_traceis set and the syscall number is not 101 (ptraceitself), the process is stopped withSIGTRAP|0x80and frame kind 1. - On exit, after storing the return value, the process is stopped again with
SIGTRAP|0x80and frame kind 1. - The
ptrace_in_syscallflag is set on entry and cleared on exit to distinguish entry stops from exit stops.
Last reviewed: 2026-07-22