---
title: ptrace とデバッグインターフェース
url: https://doc.liz6.com/ja/systems-programming/04-signals-and-exceptions/02-ptrace-and-debugging-interfaces
locale: ja
area: systems-programming
tags:
- systems-programming
- signals-and-exceptions
date: 2026-06-30
modified: 2026-07-16
description: '対象範囲: ptrace(PTRACE_ATTACH/PTRACE_SYSCALL/PTRACE_PEEKDATA/PTRACE_GETREGS) → strace/gdb の実装原理 → /proc/pid/mem → seccomp user notify 適用環境: Linux ユーザーランド, x86-64/ARM64'
---

# 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 のコア操作

```c
#include <sys/ptrace.h>

// アタッチ（自身または他者）:
ptrace(PTRACE_TRACEME, 0, 0, 0);            // トレシーが「トレース可能」であることを宣言
// または
ptrace(PTRACE_ATTACH, target_pid, 0, 0);    // トレーサーがトレシーにアタッチ
ptrace(PTRACE_SEIZE, target_pid, 0, opts);  // アタッチするが停止しない (SEIZE, 3.4以降)

// 制御:
ptrace(PTRACE_CONT, pid, 0, sig);           // 実行を再開（任意のシグナル付与）
ptrace(PTRACE_SYSCALL, pid, 0, 0);          // システムコールのエントリとアウトプットの両方で停止
ptrace(PTRACE_SINGLESTEP, pid, 0, 0);       // 1命令ずつステップ実行

// 読み書き:
ptrace(PTRACE_PEEKDATA, pid, addr, 0);      // トレシーのメモリを読み取る（ワード単位）
ptrace(PTRACE_POKEDATA, pid, addr, data);   // トレシーのメモリに書き込む
ptrace(PTRACE_GETREGS, pid, 0, &regs);      // レジスタを読み取る
ptrace(PTRACE_SETREGS, pid, 0, &regs);      // レジスタに書き込む

// デタッチ:
ptrace(PTRACE_DETACH, pid, 0, 0);
```

## strace の原理

```c
// strace のコアロジック（簡略化版）:

// 1. 子プロセスをfork
pid = fork();
if (pid == 0) {
    ptrace(PTRACE_TRACEME, 0, 0, 0);
    execve(argv[1], &argv[1], envp);
}

// 2. 親プロセス（トレーサー）:
waitpid(pid, &status, 0);  // 最初の停止（execve 後の SIGTRAP）を待つ

while (1) {
    ptrace(PTRACE_SYSCALL, pid, 0, 0);   // カーネルに指示: システムコール時に停止
    waitpid(pid, &status, 0);             // 停止を待つ

    if (WIFEXITED(status)) break;

    // トレシーがシステムコールのエントリで停止している → 引数を読み取る
    ptrace(PTRACE_GETREGS, pid, 0, &regs);
    // x86: regs.orig_rax = システムコール番号, regs.rdi/rsi/rdx = 引数 0-2

    // システムコールのアウトプットまで継続:
    ptrace(PTRACE_SYSCALL, pid, 0, 0);
    waitpid(pid, &status, 0);
    ptrace(PTRACE_GETREGS, pid, 0, &regs);
    // regs.rax = 戻り値（-errno である可能性あり）
}
```

システムコールのエントリ/アウトプットごとに、以下の順序で処理が行われます:
トレーサー → 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 の代替手段

```c
// ptrace ATTACH が不要 → tracee の仮想メモリを読み書き可能
// ただし、rw 以外の領域への書き込みには ptrace ATTACH が必要（5.x 以前）
// 5.x 以降: process_vm_readv/writev がより効率的（/proc/pid/mem を開く必要がない）

int memfd = open("/proc/<pid>/mem", O_RDWR);
pread(memfd, buf, size, addr);  // tracee のアドレス addr から読み取り
pwrite(memfd, buf, size, addr); // tracee のアドレス addr に書き込み
```

## seccomp user notify: 「新時代の ptrace」

```c
// 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*
