執筆者:木村
家庭用ゲーム開発会社を経てモノリスソフトへ入社。 以来、プログラマーとして主にエフェクトエンジン開発、開発環境支援の業務を担当。 好きな食べ物はたこ焼き。
TECH BLOG
こんにちは。モノリスソフト プログラマーの木村です。
C++プログラムのメモリバグ検出ツールである Address Santizer(以降ASan)について調べましたので紹介します。
ASanを導入することで、コードを大きく変更することなく、容易かつ効果的にメモリバグを検出できる可能性があります。
ASanは、ランタイムのメモリバグ検出ツールです。
LLVM(v3.1~)でサポートされ、VisualStudio2019(v16.9~)でもサポートされるようになりました。
ASanによって以下のようなメモリバグを検出できます。
ASanではざっくり以下の方法でメモリチェックを行います。
ASanには以下の制限があります。
カスタムアロケータの制限については、カスタムアロケータ用の対応コードが提供されており、これを実装することでメモリバグの検出が可能になります。
後の章に参考情報を記載しています。
VisualStudio2022(v17.5)でASanの動作確認を行いました。
#include <memory>
void StackBufferOverflow() { int stack_buff[100]; for (int i = 0; i <= 100; ++i) { stack_buff[i] = i; } } void HeapBufferOverflow() { int* heap_buff = static_cast<int*>(malloc(sizeof(int) * 100)); *(heap_buff - 1) = 5; free(heap_buff); } int main() { StackBufferOverflow(); HeapBufferOverflow(); return 0; }
StackBufferOverflow |
---|
エラー出力画面 |
エラー出力画面の補足説明 ・青色ライン: ・検出したメモリバグの概要 ・スタックバッファオーバーフロー ・緑色ライン: ・検出した状況のスタック情報やコード情報 ・紫色ライン: ・検出時のシャドウメモリの状態 ・凡例よりスタックの右レッドゾーン(範囲外)アクセスとわかる |
HeapBufferOverflow |
---|
エラー出力画面 |
エラー出力画面の補足説明 ・青色ライン: ・検出したメモリバグの概要 ・ヒープバッファオーバーフロー ・緑色ライン: ・ヒープのメモリサイズやコールスタック ・紫色ライン: ・検出時のシャドウメモリの状態 ・凡例よりヒープの左レッドゾーン(範囲外)アクセスとわかる |
ヒープの解放後アクセス |
---|
サンプルコード+エラー出力画面 |
エラー出力画面の補足説明 ・青色ライン: ・検出したメモリバグの概要 ・解放後アクセス ・緑色ライン: ・フリー時と前アロケート時のコールスタック ・ヒープのメモリサイズ ・紫色ライン: ・検出時のシャドウメモリの状態 ・凡例より解放後アクセスとわかる |
Asanでは、カスタムアロケータ用にメモリ領域を毒化/無毒化するインタフェースを提供しています。
これを実装することでASanでメモリバグを検出できます。
【用語解説】毒化・無毒化
ASanのドキュメントやコードで使用されている POISON / UNPOISON を訳したものです。メモリの状態を表していて、POISONにしたメモリ領域にアクセスした場合はメモリバグとして検出されます。UNPOISONにすると、メモリ領域に問題なくアクセスできるようになります。
ASan有効時には、カスタムアロケータの実装で、ASanが提供している毒化/無毒化コードを呼び出します。
また、メモリ要件(メモリとシャドウメモリの対応)よりメモリ領域を8バイト境界に揃える必要があります。
カスタムアロケータへの組み込みサンプルとして、以下をご参考ください。
Microsoft Learn、AddressSanitizer ランタイム
https://learn.microsoft.com/ja-jp/cpp/sanitizers/asan-runtime?view=msvc-170#alignment-requirements-for-addresssanitizer-poisoning
カスタムアロケータでの毒化/無毒化アクセス |
---|
エラー出力画面 |
エラー出力画面の補足説明 ・青色ライン: ・検出したメモリバグの概要 ・範囲外・解放後アクセスでも毒化後アクセスとして検出 ・緑色ライン: ・アクセス情報やコールスタックやメモリサイズ ・紫色ライン: ・検出時のシャドウメモリの状態 ・ユーザによる毒化とわかる |
ASan有効時には、カスタムアロケータの実装を、malloc/freeなどの一般的なメモリ関数を呼び出すようにします。
組み込みは比較的容易ですが、パフォーマンスが大幅に低下する可能性があります。
特定のアロケータタイプやアロケータインスタンスだけで本対応を有効化できるようにするなど対策が考えられます。
ASanの概要とVisualStudio2022での動作確認、カスタムアロケータでのASan対応などを紹介させていただきました。
ASanを有効化するだけでランタイムのメモリバグ検出ができること、また、カスタムアロケータを使う場合でもメモリバグを検出できることを確認しました。
調査して以下のように考えました。
今回はASanを紹介しましたが、Sanitizerには UndefinedBehaviorSanitizer(未定義動作の検出)やMemorySanitizer(AddressSanitizerで検出できない未初期化メモリ読み取りの検出)などのデバッグツールもあります。
他のSanitizerも含めてデバッグツールの活用を検討いただければと思います。
本記事が皆様のゲーム開発ライフに役立てば幸いです。
Microsoft Learn、AddressSanitizer
https://learn.microsoft.com/ja-jp/cpp/sanitizers/asan?view=msvc-170
Clang 18.0.0git documentation、AddressSanitizer
https://clang.llvm.org/docs/AddressSanitizer.html
GitHub、AddressSanitizer
https://github.com/google/sanitizers/wiki/AddressSanitizer
Microsoft Learn、AddressSanitizer ランタイム
https://learn.microsoft.com/ja-jp/cpp/sanitizers/asan-runtime?view=msvc-170#alignment-requirements-for-addresssanitizer-poisoning
執筆者:木村
家庭用ゲーム開発会社を経てモノリスソフトへ入社。 以来、プログラマーとして主にエフェクトエンジン開発、開発環境支援の業務を担当。 好きな食べ物はたこ焼き。