このページの目次
ptrace とデバッグインターフェース
対象範囲: ptrace(PTRACE_ATTACH/PTRACE_SYSCALL/PTRACE_PEEKDATA/PTRACE_GETREGS) → strace/gdb の実装原理 → /proc/pid/mem → seccomp user notify 適用環境: Linux ユーザーランド, x86-64/ARM64
概要
ptrace は Unix で最も古いデバッグインターフェースであり、トレーサープロセスがトレシープロセスを完全に制御することを可能にします。gdb(ブレークポイント、ステップ実行、レジスタの読み書き)や strace(システムコールのトレース)は、いずれも ptrace を基盤として構築されています。近年ではより効率的な代替手段(/proc/pid/mem、seccomp user notify)が登場していますが、ptrace は依然として最も汎用性の高いインターフェースです。
ptrace のコア操作
// アタッチ(自身または他者):
; // トレシーが「トレース可能」であることを宣言
// または
; // トレーサーがトレシーにアタッチ
; // アタッチするが停止しない (SEIZE, 3.4以降)
// 制御:
; // 実行を再開(任意のシグナル付与)
; // システムコールのエントリとアウトプットの両方で停止
; // 1命令ずつステップ実行
// 読み書き:
; // トレシーのメモリを読み取る(ワード単位)
; // トレシーのメモリに書き込む
; // レジスタを読み取る
; // レジスタに書き込む
// デタッチ:
;
strace の原理
// strace のコアロジック(簡略化版):
// 1. 子プロセスをfork
pid = ;
if
// 2. 親プロセス(トレーサー):
; // 最初の停止(execve 後の SIGTRAP)を待つ
while
システムコールのエントリ/アウトプットごとに、以下の順序で処理が行われます: トレーサー → PTRACE_SYSCALL → waitpid → GETREGS → PTRACE_SYSCALL → waitpid → GETREGS。 これが strace のオーバーヘッドが大きい根本原因です。トレース対象のシステムコールごとに、4回のコンテキストスイッチ(tracee→tracer→tracee→tracer)が発生します。
gdb の原理
ブレークポイント:
gdb は PTRACE_PEEKDATA を使用して、ブレークポイント位置の命令を読み取り → 保存
PTRACE_POKEDATA を使用して、その位置を INT3 (0xCC) に書き換え
INT3 に到達 → SIGTRAP → トレーサーが通知される (waitpid)
トレーサーが元の命令を復元 → ステップ実行または継続
ステップ実行:
PTRACE_SINGLESTEP → CPU が1命令実行 → SIGTRAP → トレーサー
レジスタの読み書き:
PTRACE_GETREGS / PTRACE_SETREGS
メモリの読み書き:
PTRACE_PEEKDATA / PTRACE_POKEDATA(低速、ワード単位)
/proc/pid/mem: より効率的(pread/pwrite、任意のサイズ)
/proc/pid/mem: ptrace の代替手段
// ptrace ATTACH が不要 → tracee の仮想メモリを読み書き可能
// ただし、rw 以外の領域への書き込みには ptrace ATTACH が必要(5.x 以前)
// 5.x 以降: process_vm_readv/writev がより効率的(/proc/pid/mem を開く必要がない)
int memfd = ;
; // tracee のアドレス addr から読み取り
; // tracee のアドレス addr に書き込み
seccomp user notify: 「新時代の ptrace」
// SECCOMP_RET_USER_NOTIF: seccomp フィルターがシステムコールを監視プロセスに通知
// 監視プロセスはチェック/修正/承認/拒否が可能 → ptrace が不要(オーバーヘッドが大幅に低い)
// ptrace の問題点:
// すべてのシステムコールで停止 → トレーサー → tracee(関心のないシステムコールでも)
// seccomp user notify:
// seccomp フィルターに一致するシステムコールのみをインターセプト → その他は影響を受けない
// → ptrace のオーバーヘッドは O(すべてのシステムコール)、seccomp は O(関心のあるもの)
デバッガーの制限
1. 1つのプロセスにはトレーサーが1つしか持てない(複数の strace は不可)
2. PTRACE_TRACEME 実行後のプロセスは setuid できない(セキュリティ制限)
3. init (PID 1) はトレースできない
4. Yama LSM: /proc/sys/kernel/yama/ptrace_scope でトレース可能な対象を制限
0: 誰でも誰かをトレース可能(緩い)
1: 自身または子孫のみをトレース可能(デフォルト)
2: 子孫のみをトレース可能(管理者によるオーバーライド可)
3: ptrace を完全に禁止
参考
- man: ptrace(2)
- ソースコード:
kernel/ptrace.c,kernel/seccomp.c - LWN: "Better ptrace", "Seccomp user-space notification"
キーワード: ptrace, PTRACE_ATTACH, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_PEEKDATA, strace, gdb, /proc/pid/mem, seccomp user notify, Yama