AddressSanitizer ランタイム
AddressSanitizer ランタイム ライブラリは、メモリ アクセスの検査を有効にするために、一般的なメモリ割り当て関数と操作をインターセプトします。 コンパイラによって生成される可能性のあるさまざまな種類の実行可能ファイルをサポートする、さまざまランタイム ライブラリがあります。 コンパイラとリンカーは、 /fsanitize=address オプションをコンパイル時に渡す限り、適切なランタイム ライブラリを自動的にリンクします。 この既定の動作は、/NODEFAULTLIB オプションをリンク時に使用することでオーバーライドできます。 詳細については、「AddressSanitizer の言語、ビルド、デバッグのリファレンス」のリンクに関するセクションを参照してください。
cl /fsanitize=addressを使用してコンパイルすると、コンパイラによって、shadow バイトを管理およびチェックする命令が生成。 プログラムは、このインストルメンテーションを使用して、スタック上、ヒープ内、またはグローバル スコープでのメモリ アクセスを調べます。 コンパイラは、スタック変数とグローバル変数を記述するメタデータも生成します。 このメタデータにより、ランタイムは正確なエラー診断 (ソース コード内の関数名、行、列) を生成できるようになります。 コンパイラ チェックとランタイム ライブラリを組み合わせることで、さまざまな種類のメモリ安全性バグを、実行時に発生した場合に、正確に診断できます。
Visual Studio 17.7 Preview 3 以降、AddressSanitizer ランタイムにリンクするためのランタイム ライブラリの一覧を次に示します。 /MT (ランタイムを静的にリンクする) および/MD (実行時に再リストを動的にリンクする) オプションの詳細については、「/MD、/MT、/LD (ランタイム ライブラリの使用)」を参照してください。
Note
次の表では、 {arch} は i386 または x86_64です。
これらのライブラリは、アーキテクチャ名に Clang 規則を使用します。 MSVC 規則は、通常、i386やx86_64ではなく、x86でx64されますが、同じアーキテクチャを参照します。
| CRT オプション | AddressSanitizer ランタイム ライブラリ (.lib) | ランタイム バイナリのアドレス指定 (.dll) |
|---|---|---|
/MT または /MTd |
clang_rt.asan_dynamic-{arch}, clang_rt.asan_static_runtime_thunk-{arch} |
clang_rt.asan_dynamic-{arch} |
/MD または /MDd |
clang_rt.asan_dynamic-{arch}, clang_rt.asan_dynamic_runtime_thunk-{arch} |
clang_rt.asan_dynamic-{arch} |
次の図は、 /MT、 /MTd、 /MD、および /MDd コンパイラ オプションの言語ランタイム ライブラリがどのようにリンクされているかを示しています。
この図は、ランタイム ライブラリをリンクするための 3 つのシナリオを示しています。 1 つ目は /MT または /MTd です。 My_exe.exeとmy_dll.dllはどちらも、静的にリンクされた VCRuntime、ユニバーサル CRT、および C++ ランタイムの独自のコピーと共に表示されます。 このシナリオでは、my_exe.exeとmy_dll.dllの両方がvcruntime140.dll、ucrtbase.dll、およびmsvcp140.dllを共有する /MD を示します。 最後のシナリオでは、my_exe.exeとmy_dll.dllの両方がランタイムのデバッグ バージョン (vcruntime140d.dll、ucrtbased.dll、msvcp140d.dll) を共有する /MDd を示します。
次の図は、さまざまなコンパイラ オプションに対して ASan ライブラリがどのようにリンクされているかを示しています。
この図は、ASan ランタイム ライブラリをリンクするための 4 つのシナリオを示しています。 シナリオは、/MT (ランタイムを静的にリンクする)、/MTd (デバッグ ランタイムを静的にリンクする)、/MD (実行時に再リストを動的にリンクする)、/MDd (実行時にデバッグの再リストを動的にリンクする) です。 いずれの場合も、リンクとその関連付けmy_exe.exe、clang-rt.asan-dynamix-x86_64.dllの 1 つのインスタンスへのリンクmy_dll.dll。
静的にリンクする場合でも、ASan ランタイム DLL は、他の C ランタイム コンポーネントとは異なり、実行時に存在する必要があります。
以前のバージョン
Visual Studio 17.7 Preview 3 より前では、静的にリンクされた (/MT または /MTd) ビルドでは DLL 依存関係が使用されませんでした。 代わりに、AddressSanitizer ランタイムはユーザーの EXE に静的にリンクされていました。 その後、DLL プロジェクトはユーザーの EXE からエクスポートを読み込んで、ASan 機能にアクセスします。
動的にリンクされたプロジェクト (/MD または /MDd) では、プロジェクトがデバッグ用とリリース用に構成されているかどうかに応じて、異なるライブラリと DLL が使用されていました。 これらの変更とその動機の詳細については、「 MSVC Address Sanitizer – 1 DLL for all Runtime Configurationsを参照してください。
次の表では、Visual Studio 17.7 Preview 3 より前の AddressSanitizer ランタイム ライブラリ リンクの以前の動作について説明します。
| CRT オプション | DLL または EXE | DEBUG? | ASan ライブラリ (.lib) |
ASan ランタイム バイナリ (.dll) |
|---|---|---|---|---|
/MT |
EXE | いいえ | clang_rt.asan-{arch}, clang_rt.asan_cxx-{arch} |
なし |
/MT |
[DLL] | いいえ | clang_rt.asan_dll_thunk-{arch} |
なし |
/MD |
いずれか | いいえ | clang_rt.asan_dynamic-{arch}, clang_rt.asan_dynamic_runtime_thunk-{arch} |
clang_rt.asan_dynamic-{arch} |
/MT |
EXE | はい | clang_rt.asan_dbg-{arch}, clang_rt.asan_dbg_cxx-{arch} |
なし |
/MT |
[DLL] | はい | clang_rt.asan_dbg_dll_thunk-{arch} |
なし |
/MD |
接続前/接続後 | はい | clang_rt.asan_dbg_dynamic-{arch}, clang_rt.asan_dbg_dynamic_runtime_thunk-{arch} |
clang_rt.asan_dbg_dynamic-{arch} |
次の図は、Visual Studio 2022 17.7 Preview 3 より前に、さまざまなコンパイラ オプションで ASan ライブラリがどのようにリンクされていたかを示しています。
この図は、ASan ランタイム ライブラリをリンクするための 4 つのシナリオを示しています。 シナリオは、/MT (ランタイムを静的にリンクする)、/MTd (デバッグ ランタイムを静的にリンクする)、/MD (実行時に再リストを動的にリンクする)、/MDd (実行時にデバッグの再リストを動的にリンクする) です。 /MT の場合、my_exe.exeには ASan ランタイムの静的にリンクされたコピーがあります。 my_exe.exeの ASan ランタイムへのリンクをmy_dll.dllします。 /MTd の場合、静的にリンクされたデバッグ ASan ランタイムを使用する点が異なります。 /MD の場合、my_exe.exeとmy_dll.dllの両方が、clang_rt.asan_dynamic-x86_64.dllという名前の動的にリンクされた ASan ランタイムにリンクされます。 /MDd の場合、clang_rt.asan_dbg_dynamic-x86_64.dllという名前のデバッグ ASan ランタイムへのmy_exe.exeとmy_dll.dllリンクを除き、図は同じです。
関数のインターセプト
AddressSanitizer は、多くのホットパッチ手法を使用して関数インターセプトを実現します。 これらの手法は、ソースコード自体内に最もよく記述されています。
ランタイム ライブラリは、数多くの一般的なメモリ管理およびメモリ操作関数をインターセプトします。 一覧については、「AddressSanitizer のインターセプトした関数の一覧」を参照してください。 割り当てインターセプターは、それぞれの割り当て呼び出しに関連するメタデータとシャドウ バイトを管理します。 malloc や delete のような CRT 関数が呼び出されるたびに、インターセプターは特定の値を AddressSanitizer シャドウメモリ領域内に設定して、これらのヒープ位置に現在アクセスできるかどうかと、割り当ての境界は何かを示します。 これらのシャドウバイトにより、コンパイラによって生成されるシャドウバイトのチェックによって、読み込みまたはストアが有効かどうかを調べることができます。
インターセプトが成功するとは限りません。 関数のプロローグが短すぎて jmp を書き込むことができない場合、インターセプトは失敗する可能性があります。 インターセプトエラーが発生した場合、プログラムはを debugbreak スローして停止します。 デバッガーをアタッチすると、インターセプト問題の原因が明確になります。 この問題が発生した場合は、バグを報告してください。
Note
ユーザーは、必要に応じて、環境変数 ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE を任意の値に設定して、失敗したインターセプトの後も継続しようと試みることができます。 インターセプト エラーの後も継続すると、その関数のバグ レポートが失われる可能性があります。
カスタム アロケーターと AddressSanitizer ランタイム
AddressSanitizer ランタイムは、一般的なアロケーター インターフェイス、malloc/free、new/delete、HeapAlloc/HeapFree (RtlAllocateHeap/RtlFreeHeap を経由) のインターセプターを提供します。 数多くのプログラムが、何らかの理由でカスタム アロケーターを使用しています。たとえば、dlmalloc を使用したプログラムや、std::allocator インターフェイスと VirtualAlloc() を使用したソリューションです。 コンパイラは、シャドウメモリ管理呼び出しをカスタム アロケーターに自動的に追加することができません。 ユーザーは、 提供された手動のポイズニング インターフェイスを使用する必要があります。 この API により、これらのアロケーターが、既存の AddressSanitizer ランタイムおよびシャドウ バイト規則を使用して正常に機能するようになります。
手動の AddressSanitizer ポイズニング インターフェイス
Enlightening のインターフェイスは単純ですが、ユーザーにアラインメントの制限が課せられます。 ユーザーは、sanitizer/asan_interface.h をインポートすることでこれらのプロトタイプをインポートできます。 インターフェイス関数のプロトタイプを下に示します。
void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
便宜上、AddressSanitizer インターフェイス ヘッダー ファイルにはラッパー マクロが用意されています。 これらのマクロは、AddressSanitizer 機能がコンパイル中に有効になっているかどうかを調べます。 これにより、ソース コードでポイズニング関数呼び出しが不要になったときに省略できます。 上記の関数を直接呼び出すよりも、これらのマクロを使用することをお勧めします。
#define ASAN_POISON_MEMORY_REGION(addr, size)
#define ASAN_UNPOISON_MEMORY_REGION(addr, size)
AddressSanitizer ポイズニングのアラインメント要件
シャドウバイトの手動ポイズニングでは、アラインメント要件を考慮する必要があります。 ユーザーはパディングを必要に応じて追加して、シャドウ バイトがシャドウ メモリ内のバイト境界で終了するようにしなければなりません。 AddressSanitizer シャドウ メモリ内の各ビットは、アプリケーションのメモリ内の 1 バイトの状態をエンコードします。 このエンコーディングは、各割り当ての合計サイズ (パディングを含む) を8バイト境界に揃える必要があることを意味します。 アライメント要件が満たされていない場合、バグ報告が正しくなくなる可能性があります。 誤ったレポートは、欠落レポート (偽陽性) として、またはエラーなしのレポート (偽陽性) を示すことがあります。
アラインメントの要件と潜在的な問題の説明は、提供されている ASan アラインメントの例を参照してください。 1 つは、手動シャドウ メモリ ポイズニングで何が問題になるかを示す小さなプログラムです。 2 つ目は、std::allocator インターフェイスを使用し た手動ポイズニングの実装例です。
ランタイム オプション
Microsoft C/C++ (MSVC) は、 llvm-プロジェクト リポジトリの Clang AddressSanitizer ランタイムに基づくランタイムを使用します。 このため、ほとんどのランタイム オプションが 2 つのバージョン間で共有されます。 パブリック Clang ランタイム オプションの完全な一覧は、こちらにあります。 いくつかの違いについて、以下のセクションで説明します。 期待どおりに機能しないオプションが見つかった場合は、バグを報告してください。
サポートされていない AddressSanitizer オプション
- detect_container_overflow
- unmap_shadow_on_exit
Note
AddressSanitizer ランタイム オプション halt_on_error は、期待どおりに機能しません。 Clang と MSVC の両方のランタイム ライブラリで、数多くのエラーの種類が、ほとんどのメモリ破損エラーを含めて、継続不可能と見なされています。
詳細については、「Clang 12.0 との違い」を参照してください。
MSVC 固有の AddressSanitizer ランタイム オプション
windows_hook_legacy_allocatorsブール値。GlobalAllocおよびLocalAllocアロケーターのインターセプトを無効にするfalseに設定します。Note
この記事が記述されたとき、オプション
windows_hook_legacy_allocatorsはパブリック llvm プロジェクト ランタイムで使用できませんでした。 このオプションは、最終的にパブリック プロジェクトに反映される可能性がありますが、コード レビューとコミュニティの同意に依存しています。オプション
windows_hook_rtl_allocatorsは、以前は AddressSanitizer が試験段階であるためオプトイン機能でしたが、いまは既定で有効になりました。 Visual Studio 2022 バージョン 17.4.6 より前のバージョンでは、既定のオプション値はfalse。 Visual Studio 2022 バージョン 17.4.6 以降のバージョンでは、オプションwindows_hook_rtl_allocators既定でtrue。iat_overwrite文字列。既定で"error"に設定されます。 その他の使用可能な値は、"protect"と"ignore"です。 一部のモジュールは、特定の関数の実装をカスタマイズするために、他のモジュールのimport address tableを上書きすることがあります。 たとえば、ドライバーは通常、特定のハードウェアのカスタム実装を提供します。iat_overwriteオプションは、特定のmemoryapi.h関数の上書きに対する AddressSanitizer ランタイムの保護を管理します。 ランタイムは現在、保護のためにVirtualAlloc、VirtualProtect、およびVirtualQuery関数を追跡します。 このオプションは、Visual Studio 2022 バージョン 17.5 Preview 1 以降のバージョンで使用できます。 次のiat_overwrite値は、保護された関数が上書きされたときにランタイムがどのように反応するかを制御します。"error"(既定値) に設定すると、上書きが検出されるたびにランタイムからエラーが報告されます。"protect"に設定すると、ランタイムは上書きされた定義の使用を回避して続行します。 実質的に、関数の元のmemoryapi定義は、無限再帰を回避するためにランタイム内から使用されます。 プロセス内の他のモジュールでは、上書きされた定義が引き続き使用されます。"ignore"に設定すると、ランタイムは上書きされた関数の修正を試みず、実行を続行します。
windows_fast_fail_on_errorブール値 (既定では false) をtrueに設定すると、エラー レポートの印刷後にプロセスを __fastfail(71) で終了できます。
Note
abort_on_error値が true に設定されている場合、Windows ではプログラムは exit(3) で終了します。 現在の動作を変更しないように、代わりにこの新しいオプションを導入することにしました。 abort_on_errorとwindows_fast_fail_on_errorの両方が true の場合、プログラムは__fastfailで終了します。
AddressSanitizer のインターセプトした関数の一覧 (Windows)
AddressSanitizer ランタイムは、実行時にメモリの安全性チェックを有効にするために、多くの関数をホットパッチします。 AddressSanitizer ランタイムが監視する関数の包括的でない一覧を次に示します。
既定のインターセプター
__C_specific_handler(x64 のみ)_aligned_free_aligned_malloc_aligned_msize_aligned_realloc_calloc_base_calloc_crt_calloc_dbg(デバッグ ランタイムのみ)_except_handler3(x86 のみ)_except_handler4(x86 のみ) (ドキュメントに未記載)_expand_expand_base(ドキュメントに未記載)_expand_dbg(デバッグ ランタイムのみ)_free_base(ドキュメントに未記載)_free_dbg(デバッグ ランタイムのみ)_malloc_base(ドキュメントに未記載)_malloc_crt(ドキュメントに未記載)_malloc_dbg(デバッグ ランタイムのみ)_msize_msize_base(ドキュメントに未記載)_msize_dbg(デバッグ ランタイムのみ)_realloc_base(ドキュメントに未記載)_realloc_crt(ドキュメントに未記載)_realloc_dbg(デバッグ ランタイムのみ)_recalloc_recalloc_base(ドキュメントに未記載)_recalloc_crt(ドキュメントに未記載)_recalloc_dbg(デバッグ ランタイムのみ)_strdupatoiatolcallocCreateThreadfreefrexplongjmpmallocmemchrmemcmpmemcpymemmovememsetRaiseExceptionreallocRtlAllocateHeapRtlCreateHeapRtlDestroyHeapRtlFreeHeapRtlRaiseExceptionRtlReAllocateHeap(ドキュメントに未記載)RtlSizeHeap(ドキュメントに未記載)SetUnhandledExceptionFilterstrcatstrchrstrcmpstrcpystrcspnstrdupstrlenstrncatstrncmpstrncpystrnlenstrpbrkstrspnstrstrstrtokstrtolwcslenwcsnlen
オプションのインターセプター
ここに示すインターセプターがインストールされるのは、AddressSanitizer ランタイム オプションが有効になっているときだけです。 レガシ アロケーターのインターセプトを無効にするには、 windows_hook_legacy_allocators を false に設定します。
set ASAN_OPTIONS=windows_hook_legacy_allocators=false
GlobalAllocGlobalFreeGlobalHandleGlobalLockGlobalReAllocGlobalSizeGlobalUnlockLocalAllocLocalFreeLocalHandleLocalLockLocalReAllocLocalSizeLocalUnlock
関連項目
AddressSanitizer の概要
AddressSanitizer の既知の問題
AddressSanitizer のビルドと言語リファレンス
AddressSanitizer シャドウ バイト
AddressSanitizer クラウドまたは分散テスト
AddressSanitizer デバッガーの統合
AddressSanitizer エラーの例