最終更新:2026-10-12
AddressSanitizer メモリ検出¶
CとC++は非常に安全でなくエラーを起こしやすいプログラミング言語です。Address SanitizerはGoogleが開発した、解放後使用(use-after-free)やメモリリークなどのメモリアクセスエラーを検出するためのツールです。GCCバージョン>= 4.8に統合されており、CとC++のコードに使用できます。Address Sanitizerはメモリ割り当てを追跡するためにランタイムインストルメンテーションを使用します。つまり、その機能をフルに活用するには、AddressSanitizerを使ってコードをビルドする必要があります。
- Use after free (dangling pointer dereference)
- Heap buffer overflow
- Stack buffer overflow
- Global buffer overflow
- Use after return
- Use after scope
- Initialization order bugs
- Memory leaks
本記事では、上記の8つのシナリオに沿って、メモリ検出ツール**AddressSanitizer**を紹介します。
1.Use after free¶
以下のコードを例にとります。まず、newキーワードによって整数1つのメモリ領域を割り当て、そのアドレスをポインタptrに代入します。その後、deleteキーワードを使ってこのメモリを解放します。しかし、解放後もプログラムはポインタptrを通じて、すでに解放されたメモリへのアクセスを試み、そのメモリ位置に値10を代入しています。これが解放後使用エラーです。
#include <iostream>
int main() {
int* ptr = new int;
delete ptr;
*ptr = 10; // Use after free
return 0;
}
コンパイル時に次のオプションを追加します。
生成されたbinファイルを実行すると、次の結果が得られます。

2.heap buffer overflow¶
まず、newキーワードによってヒープ上に整数2つの配列を割り当て、その配列の先頭アドレスをポインタarrに代入します。その後、プログラムは値10を配列の3番目の位置(インデックス2)に格納しようとします。これは配列の有効範囲を超えており、ヒープバッファオーバーフローエラーにつながります。
#include <iostream>
int main() {
int* arr = new int[2];
arr[2] = 10; // Heap buffer overflow
delete[] arr;
return 0;
}
上と同じ使い方です:
その後:
次の検出結果が得られます。

3.stack buffer overflow¶
vulnerableFunction関数の中では、8文字のchar配列bufferが定義されています。その後、std::cinを使って標準入力からデータを読み取りbufferに格納しますが、入力サイズの制限がありません。入力された文字数がbufferのサイズを超えると、スタックバッファオーバーフローエラーが発生します。
void vulnerableFunction() {
char buffer[8];
std::cin >> buffer; // Stack buffer overflow
std::cout << "You entered: " << buffer << std::endl;
}
使い方は上と同じです。

4.global buffer overflow¶
グローバルバッファ(buffer配列)はプログラムのグローバル領域で定義されており、入力サイズに対する十分な制限がありません。入力された文字数がバッファのサイズを超えると、グローバルバッファオーバーフローエラーが発生します。スタックバッファオーバーフローと同様に、グローバルバッファオーバーフローはプログラムの不安定化やセキュリティ問題を引き起こす可能性があります。
#include <iostream>
char buffer[8];
int main() {
std::cin >> buffer; // 全局缓冲区溢出错误
std::cout << "You entered: " << buffer << std::endl;
return 0;
}
使い方は上と同じです。次が得られます:

5.use after return¶
関数fの中では、ローカル変数iのアドレスが返されていますが、main関数の中で、返されたポインタを通じて、すでに破棄されたローカル変数にアクセスしようとしています。このようなシナリオでは、binファイルを検出し、ASAN_OPTIONS=detect_stack_use_after_return=1を設定します。
g++-13 stack_use_after_return.cc -Wall -g -fsanitize=address
ASAN_OPTIONS=detect_stack_use_after_return=1 ./a.out
コード:
すると、次の結果が得られます:

6.use after scope¶
ローカル変数xのアドレスをポインタpに代入します。しかし、この内部スコープを抜けた後、ポインタpを通じて、すでに範囲外となったローカル変数xにアクセスしようとしており、スコープ外使用エラーが発生します。これも未定義動作です。
コンパイルと実行:
g++-13 stack_use_after_scope.cc -Wall -g -fsanitize=address
ASAN_OPTIONS=detect_stack_use_after_scope=0 ./a.out
実際に試したところ、clang++では検出できますが、g++では検出できません。

7.Initialization order bugs¶
2つの異なるソースファイルの中に、グローバル変数aの初期化が存在しますが、初期化の順序は不定です。init_order_b.ccファイルの中では、変数aの値を使用しようとしていますが、初期化順序の不整合により、未定義の動作が引き起こされる可能性があります。
コードは次のとおりです:
// init_order_a.cc
int duplicate(int n) { return n * 2; }
auto a = duplicate(2);
// init_order_b.cc
#include <iostream>
extern int a;
auto b = a;
int main() {
std::cout << b << std::endl;
return 0;
}
このようなコードを書いたとき、その結果は不定であり、初期化の順序によって4になることも0になることもあります。
例えば:
この順序なら出力は4ですが、先にbをコンパイルし、後からaをコンパイルすると0が得られます。
このような問題が発生したとき、AddressSanitizerを使って初期化順序を検出できます。

8.memory leak¶
newキーワードによって整数1つのメモリを動的に割り当てましたが、プログラムが終了する前にdeleteキーワードでこのメモリを解放していないため、メモリリークが発生します。
#include <iostream>
int main() {
int* ptr = new int;
// Missing delete ptr; // Memory leak
return 0;
}
ASAN_OPTIONS=detect_leaks=1を設定します。
