---
title: Valgrind と Sanitizers の実践
url: https://doc.liz6.com/ja/systems-programming/08-performance-and-debugging/02-valgrind-and-sanitizers-in-practice
locale: ja
area: systems-programming
tags:
- systems-programming
- performance-and-debugging
date: 2026-06-30
modified: 2026-07-16
description: 'カバー範囲: Valgrind (memcheck/helgrind/cachegrind/massif) → ASAN/UBSAN/TSAN → 比較と選択 → 実際のデバッグ事例 対象: Linux ユーザー空間, C/C++'
---

# Valgrind と Sanitizers の実践

> カバー範囲: Valgrind (memcheck/helgrind/cachegrind/massif) → ASAN/UBSAN/TSAN → 比較と選択 → 実際のデバッグ事例
> 対象: Linux ユーザー空間, C/C++

## Valgrind: ダイナミックバイナリトランスレーション

Valgrind は本質的に JIT コンパイラです。プログラムの機械語を中間表現（VEX IR）にトランスレーションし、IR 上でインスツルメンテーションによる解析を行った後、再び機械語に戻して実行します。対象プログラムを再コンパイルする必要はありませんが、パフォーマンスのオーバーヘッドが大きくなります。

```bash
# メモリリークと境界外アクセスの検出:
valgrind --leak-check=full ./prog arg1 arg2

# リークの発生元を追跡:
valgrind --leak-check=full --track-origins=yes ./prog

# 出力の解釈:
#   ==12345== Invalid read of size 4          ← 境界外アクセス / use-after-free
#   ==12345==    at 0x4005A4: main (test.c:5)  ← 発生位置
#   ==12345==  Address 0x5204040 is 0 bytes after a block of size 40 alloc'd
#   ==12345==    at 0x4C2E80F: malloc (in /usr/lib/valgrind/vgpreload_memcheck.so)
#   ==12345==    by 0x40058F: main (test.c:3)  ← 割り当て位置
```

### Valgrind ツール群

```bash
# memcheck (デフォルト): メモリエラー
#   検出対象: UAF, ダブルフリー, 境界外アクセス, 未初期化値, メモリリーク

# helgrind: データ競合
valgrind --tool=helgrind ./prog

# cachegrind: CPU キャッシュのシミュレーション
valgrind --tool=cachegrind ./prog
cg_annotate cachegrind.out.<pid>  # ソースレベルでのミス率

# massif: ヒープ解析
valgrind --tool=massif ./prog
ms_print massif.out.<pid>  # 時間軸: 誰がどのくらいのメモリを割り当てたか
```

### Valgrind の制限

```
オーバーヘッド:
  memcheck: 約 20〜30 倍の低速化
  helgrind: 約 10〜20 倍の低速化
  cachegrind: 約 20〜50 倍の低速化
  massif: 約 10 倍の低速化

サポートされていないもの:
  - 静的リンクされたプログラム (ld.so と libc は置換可能である必要がある)
  - 一部の珍しいシステムコール
  - 一部の命令セット拡張 (AVX-512 は一部サポート)
```

---

## Sanitizers: コンパイル時のインスツルメンテーション

Sanitizers はコンパイル時にチェックコードを挿入し、実行時にエラーを検出します。Valgrind よりも高速ですが、再コンパイルが必要です。

```bash
# ASAN: ヒープ境界外アクセス, UAF, スタックオーバーフロー, グローバル境界外アクセス
gcc -fsanitize=address -g -O1 -o prog prog.c
./prog
# → エラー発生時: 正確な位置 + 割り当て位置 + 解放位置を出力 (カラー表示!)

# UBSAN: 未定義動作
gcc -fsanitize=undefined -o prog prog.c
# 検出対象: 整数オーバーフロー, シフトの境界外, null 間参照, 型のアライメントエラー

# TSAN: データ競合 (64-bit のみ)
gcc -fsanitize=thread -g -O1 -o prog prog.c
# → 報告: "data race on variable x between thread T1 and T2"

# LSAN: メモリリーク (ASAN に含まれている)
ASAN_OPTIONS=detect_leaks=1 ./prog

# MSAN: 未初期化メモリへの読み取り (Clang のみ)
clang -fsanitize=memory -g -o prog prog.c
```

### ASAN の原理

```
ASAN はシャドウメモリ (shadow memory) を使用します:
  8 バイトのメモリごとに 1 バイトのシャドウメモリ
  シャドウ値: 0=完全にアクセス可能, 1-7=部分的に有効, 負数=アクセス不可

メモリアクセスのたびに、コンパイラがシャドウチェックを挿入
  → シャドウ値がアクセス不可を示す場合 → ASAN がレポート
  → 割り当て領域の周囲に「レッドゾーン」(redzone) を配置 → 境界外アクセス検出
  → 解放後に「クォランティン」(quarantine) に配置 → UAF 検出

メモリオーバーヘッド: 約 2 倍 (シャドウメモリ + レッドゾーン + クォランティン)
```

### Valgrind vs Sanitizers

| | Valgrind | Sanitizers |
|---|---|---|
| 再コンパイルが必要か | いいえ (任意のバイナリ) | はい (-fsanitize=) |
| 速度 | 20〜50 倍の低速化 | 1.5〜5 倍の低速化 |
| メモリオーバーヘッド | 追加メモリなし | 2 倍 (ASAN) |
| 検出精度 | 高い (バイナリトランスレーションにより全てを可視化) | 高い (コンパイル時のインスツルメンテーション) |
| スレッド検出 | helgrind | TSAN |
| 使用シーン | ソースコードなしのブラックボックス | ソースコードあり、CI に統合 |

---

## 実践事例

```bash
# 事例 1: ヒープ境界外アクセス → ASAN
#   test.c:4: buf[100] = 0;  // buf は char[100]
gcc -fsanitize=address -g -o test test.c
./test
# ==ERROR: AddressSanitizer: heap-buffer-overflow
#     WRITE of size 1 at 0x602000000064 thread T0
#     #0 0x4007b3 in main test.c:4

# 事例 2: データ競合 → TSAN
gcc -fsanitize=thread -g -o test test.c
./test
# WARNING: ThreadSanitizer: data race
#   Write of size 4 at 0x7b0400000000 by thread T2:
#     #0 counter_thread test.c:8
#   Previous write of size 4 at 0x7b0400000000 by thread T1:
#     #0 counter_thread test.c:8

# 事例 3: 未初期化読み取り → Valgrind memcheck
valgrind ./prog
# Conditional jump or move depends on uninitialised value(s)
```

## 参考

- **ドキュメント**: https://github.com/google/sanitizers, https://valgrind.org/docs/manual
- **LWN**: "Address Sanitizer", "Thread Sanitizer"
- **ツール**: heaptrack (ヒーププロファイラ、massif の代替)

*キーワード: Valgrind, memcheck, helgrind, massif, ASAN, UBSAN, TSAN, MSAN, shadow memory, data race*
