この記事では、MSVC AddressSanitizer 言語仕様、コンパイラ オプション、リンカー オプション、および MSVC AddressSanitizer に固有の Visual Studio デバッガー統合を制御するオプションについて説明します。
MSVC AddressSanitizer ランタイムの詳細については、 ランタイム リファレンスを参照してください。 インターセプトされた関数に関する情報と、カスタム アロケーターをフックする方法についても説明します。 MSVC AddressSanitizer エラーからのクラッシュ ダンプの保存の詳細については、 クラッシュ ダンプのリファレンスを参照してください。
言語仕様
__SANITIZE_ADDRESS__
__SANITIZE_ADDRESS__ プリプロセッサ マクロは 1 が設定されているとき /fsanitize=address として定義されています。 このマクロは、MSVC AddressSanitizer ランタイムが存在するためにソース コードを条件付きで指定する場合に便利です。
#include <cstdio>
int main()
{
#ifdef __SANITIZE_ADDRESS__
printf("MSVC AddressSanitizer enabled");
#else
printf("MSVC AddressSanitizer not enabled");
#endif
return 1;
}
__declspec(no_sanitize_address)
__declspec(no_sanitize_address) 指定子を使用して、サニタイザーを関数、ローカル変数、またはグローバル変数で選択的に無効にすることができます。 この __declspec は、ランタイムの動作でなく、コンパイラの動作に影響を与えます。
#ifdef __SANITIZE_ADDRESS__
// no_sanitize_address is only defined when compiling with MSVC AddressSanitizer.
// Guard against this by checking if `__SANITIZE_ADDRESS__` is defined.
#define NO_SANITIZE_ADDRESS __declspec(no_sanitize_address)
#else
#define NO_SANITIZE_ADDRESS
#endif
NO_SANITIZE_ADDRESS
void test1()
{
int x[100];
x[100] = 5; // ASan exception not caught
}
void test2()
{
NO_SANITIZE_ADDRESS int x[100];
x[100] = 5; // ASan exception not caught
}
NO_SANITIZE_ADDRESS int g[100];
void test3()
{
g[100] = 5; // ASan exception not caught
}
コンパイラ
/fsanitize=address コンパイラ オプション
/fsanitize=address コンパイラ オプションは、コード内のメモリ参照をインストルメント化して、実行時のメモリ安全性エラーをキャッチします。 インストルメンテーションは、読み込み、ストア、スコープ、alloca、および CRT の各関数をフックします。 out-of-bounds、use-after-free、use-after-scope などの隠れたバグを検出することができます。 実行時に検出されたエラーの一覧については、 MSVC AddressSanitizer エラーの例を参照してください。
/fsanitize=address は、既存のすべての C++ または C 最適化レベル ( /Od、 /O1、 /O2、 /O2 /GLなど) と互換性があります。 このオプションを使用して生成されたコードは、静的および動的 CRT (/MD、/MDd、/MT、/MTd など) と連動します。 このコンパイラ オプションを使用して、x86 または x64 を対象とする .EXE または .DLL を作成できます。 最良の呼び出し履歴の書式設定をするためにデバッグ情報が必要です。 このコンパイラ オプションは、プロファイルガイド付き最適化ではサポートされていません。
いくつかの種類のエラー検出を示すコードの例については、 MSVC AddressSanitizer エラーの例を参照してください。
/fsanitize=fuzzer コンパイラオプション (試験段階)
/fsanitize=fuzzer コンパイラ オプションは LibFuzzer を既定のライブラリ リストに追加します。 また、次のサニタイザー カバレッジ オプションも設定します。
-
エッジ インストルメンテーション ポイント (
/fsanitize-coverage=edge) -
インライン 8 ビット カウンター (
/fsanitize-coverage=inline-8bit-counters) -
比較 (
/fsanitize-coverage=trace-cmp) -
整数除算 (
/fsanitize-coverage=trace-div)
/fsanitize=address を /fsanitize=fuzzer と使用することをお勧めします。
/fsanitize=fuzzer を指定すると、以下のライブラリが既定のライブラリ一覧に追加されます。
| ランタイム オプション | LibFuzzer ライブラリ |
|---|---|
/MT |
clang_rt.fuzzer_MT-{arch} |
/MD |
clang_rt.fuzzer_MD-{arch} |
/MTd |
clang_rt.fuzzer_MTd-{arch} |
/MDd |
clang_rt.fuzzer_MDd-{arch} |
main 関数を省略する LibFuzzer ライブラリも使用できます。 これらのライブラリを使用するときは、main を定義して LLVMFuzzerInitialize と LLVMFuzzerTestOneInput を呼び出す必要があります。 これらのライブラリのいずれかを使用するには、 /NODEFAULTLIB を指定し、ランタイムとアーキテクチャに対応する次のライブラリと明示的にリンクします。
| ランタイム オプション | LibFuzzer no_main ライブラリ |
|---|---|
/MT |
clang_rt.fuzzer_no_main_MT-{arch} |
/MD |
clang_rt.fuzzer_no_main_MD-{arch} |
/MTd |
clang_rt.fuzzer_no_main_MTd-{arch} |
/MDd |
clang_rt.fuzzer_no_main_MDd-{arch} |
/NODEFAULTLIB を指定し、これらのライブラリのいずれかを指定しない場合は、未解決の外部シンボル リンク エラーが表示されます。
/fsanitize-address-use-after-return コンパイラオプション (試験段階)
既定では、MSVC コンパイラは (Clang とは違って)、ヒープ内のフレームを割り当てて use-after-return エラーをキャッチするコードを生成しません。 MSVC AddressSanitizer を使用してこれらのエラーをキャッチするには、次の手順を実行する必要があります。
-
/fsanitize-address-use-after-returnオプションを使用してコンパイルします。 - プログラムを実行する前に、
set ASAN_OPTIONS=detect_stack_use_after_return=1を実行 してランタイム チェック オプションを設定します。
/fsanitize-address-use-after-return オプションを指定すると、ローカルが "アドレス取得" と見なされたときに、コンパイラはヒープ内でデュアル スタック フレームを使用するコードを生成します。このコードは、を単独で使用するよりも/fsanitize=address時間がかかります。 詳細と例については、「エラー: stack-use-after-return」を参照してください。
ヒープ内のデュアル スタック フレームは、それを作成した関数からの戻りの後に残っています。 ヒープ内のスロットに割り当てられた、ローカルのアドレスが、戻りの後のどこで使用されるかの例を考えてみましょう。 フェイク ヒープ フレームに関連付けられているシャドウバイトには、値 0xF9 が含まれます。 その 0xF9 は、ランタイムがエラーを報告したときの stack-use-after-return エラーを意味します。
スタック フレームがヒープ内に割り当てられて、関数戻りの後も残ります。 ランタイムはガベージ コレクションを使用して、これらのフェイク call-frame オブジェクトを、特定の間隔の後に、非同期的に解放します。 ローカルのアドレスがヒープ内の永続フレームに転送されます。 このようにして、定義関数の戻りの後で任意のローカルがいつ使用されるかをシステムは検出できます。 詳細については、Google のドキュメントに記載されている「戻り後のスタック使用のアルゴリズム」に関する説明を参照してください。
ASan 組み込み互換性ライブラリ
ASan を使用してビルドする場合、コンパイラは組み込み関数 ( memset など) を、同じ操作を完了するだけでなくメモリセーフ チェックを提供する ASan ランタイム ライブラリ ( __asan_memset など) によって提供される関数呼び出しに置き換えます。 ユーザー モード ASan の場合、Visual Studio には両方が用意されているため、コンパイラとランタイムが一緒に更新されます。
カーネル モード ASan (KASan) は Windows OS の一部であるため、コンパイラとは異なる周期で更新されます。 インストールされているバージョンの KASan でサポートされていない新しい組み込みを使用する新しいコンパイラの問題を回避するには、互換性ライブラリ (asan_compat.lib) をリンクしてリンク時の問題を回避します。
asan_compat.libを使用する場合、プログラムはサポートされていない ASan 組み込みが使用されていないかのように動作します。 新しい ASan 組み込みをサポートする新しいランタイム ライブラリとのリンクは、 asan_compat.libによって提供されるバージョンよりも優先されます。 この決定はリンク時に行われるので、対象とする OS バージョンと一致する Windows SDK によって提供される KASan ライブラリにリンクすることが不可欠です。
Visual Studio 2022 17.14 Preview 2 以降では、次のオプションがサポートされています。
- この互換性ライブラリを既定のライブラリとして含めるには、
/fsanitize-address-asan-compat-libコンパイラ オプションを使用します。 このオプションは、/fsanitize=kernel-addressを使用すると自動的に有効になります。 - この互換性ライブラリをオプトアウトするには、
/fno-sanitize-address-asan-compat-libコンパイラ オプションを使用します。
/fsanitize-address-asan-compat-libを使用して、新しいコンパイラを古いユーザー モードの ASan ランタイムとリンクすることは現在サポートされていません。
リンカー
/INFERASANLIBS[:NO] リンカー オプション
/fsanitize=address コンパイラ オプションは、実行可能ファイルにリンクする MSVC AddressSanitizer ライブラリを指定するオブジェクトをマークします。 ライブラリには、clang_rt.asan* で始まる名前が付いています。
/INFERASANLIBS リンカー オプション (既定では on) は、これらのライブラリを既定の場所から自動的にリンクします。 選択され、自動的にリンクされるライブラリを次に示します。
Note
次の表では、 {arch} は i386 または x86_64です。
これらのライブラリは、アーキテクチャ名に Clang 規則を使用します。 MSVC 規則は、通常、x86やx64ではなく、i386およびx86_64されます。 これらは、同じアーキテクチャを参照します。
| CRT オプション | MSVC AddressSanitizer ランタイム ライブラリ (.lib) | ランタイム バイナリのアドレス指定 (.dll) |
|---|---|---|
/MT または /MTd |
clang_rt.asan_dynamic-{arch}.lib、/wholearchive:clang_rt.asan_static_runtime_thunk-{arch}.lib |
clang_rt.asan_dynamic-{arch}.dll |
/MD または /MDd |
clang_rt.asan_dynamic-{arch}.lib、/wholearchive:clang_rt.asan_dynamic_runtime_thunk-{arch}.lib |
clang_rt.asan_dynamic-{arch}.dll |
リンカー オプション /INFERASANLIBS:NO を指定すると、リンカーは clang_rt.asan* ライブラリ ファイルを既定の場所からリンクできなくなります。 このオプションを使用する場合は、ライブラリ パスをビルド スクリプトに追加します。 それ以外の場合、リンカーは未解決の外部シンボル エラーを報告します。 ランタイム サンク ライブラリは、 オプションが適用された状態でリンクされている/wholearchive。
以前のバージョン
Visual Studio 17.7 Preview 3 より前では、静的にリンクされた (/MT または /MTd) ビルドでは DLL 依存関係が使用されませんでした。 代わりに、MSVC AddressSanitizer ランタイムがユーザーの EXE に静的にリンクされていました。 その後、DLL プロジェクトはユーザーの EXE からエクスポートを読み込んで、ASan 機能にアクセスします。 また、動的にリンクされたプロジェクト (/MD または /MTd) では、プロジェクトがデバッグ用に構成されているかリリース用に構成されているかに応じて、異なるライブラリと DLL が使用されていました。 これらの変更とその動機の詳細については、「 MSVC AddressSanitizer – すべてのランタイム構成に対して 1 つの DLL」を参照してください。
| CRT ランタイム オプション | DLL または EXE | MSVC AddressSanitizer ランタイム ライブラリ |
|---|---|---|
/MT |
EXE |
/wholearchive:clang_rt.asan-{arch}.lib、clang_rt.asan_cxx-{arch}.lib |
/MT |
DLL | /wholearchive:clang_rt.asan_dll_thunk-{arch}.lib |
/MD |
接続前/接続後 |
clang_rt.asan_dynamic-{arch}.lib、/wholearchive:clang_rt.asan_dynamic_runtime_thunk-{arch}.lib |
/MTd |
EXE |
/wholearchive:clang_rt.asan_dbg-{arch}.lib、clang_rt.asan_cxx_dbg-{arch}.lib |
/MTd |
DLL | /wholearchive:clang_rt.asan_dbg_dll_thunk-{arch}.lib |
/MDd |
接続前/接続後 |
/wholearchive:clang_rt.asan_dbg_dynamic-{arch}.lib、clang_rt.asan_dbg_dynamic_runtime_thunk-{arch}.lib |
Visual Studio の統合
/fno-sanitize-address-vcasan-lib コンパイラ オプション
/fsanitize=address オプションは、MSVC AddressSanitizer 例外がスローされたときに Visual Studio デバッグ エクスペリエンスを向上させるために、追加のライブラリにリンクします。 これらのライブラリは VCAsan と呼ばれます。 このライブラリを使用すると、Visual Studio でソース コードに MSVC AddressSanitizer エラーを表示できます。 また、MSVC AddressSanitizer エラー レポートが作成されたときに、実行可能ファイルでクラッシュ ダンプを生成することもできます。 詳細については、「 Visual Studio MSVC AddressSanitizer 拡張機能ライブラリ」を参照してください。
選択されるライブラリはコンパイラ オプションによって異なり、自動的にリンクされます。
| ランタイム オプション | VCAsan バージョン |
|---|---|
/MT |
libvcasan.lib |
/MD |
vcasan.lib |
/MTd |
libvcasand.lib |
/MDd |
vcasand.lib |
ただし、 /Zl (既定のライブラリ名を省略) を使用してコンパイルする場合は、ライブラリを手動で指定する必要があります。 そうしないと、未解決の外部シンボル リンク エラーが表示されます。 次にいくつか典型的な例を挙げます:
error LNK2001: unresolved external symbol __you_must_link_with_VCAsan_lib
error LNK2001: unresolved external symbol ___you_must_link_with_VCAsan_lib
向上したデバッグをコンパイル時に無効にするには、/fno-sanitize-address-vcasan-lib オプションを使用します。
ASAN_VCASAN_DEBUGGING 環境変数
/fsanitize=address コンパイラ オプションは、メモリ安全性バグを公開するバイナリを実行時に生成します。 このバイナリがコマンド ラインから開始されて、ランタイムがエラーを報告すると、エラーの詳細が出力されます。 その後に、プロセスを終了します。
ASAN_VCASAN_DEBUGGING 環境変数は、ランタイムがエラーを報告したとき Visual Studio IDE をすぐに起動するように設定できます。 このコンパイラ オプションを設定すると、エラーは、ソース コードに重なり、エラーの原因となった正確な行と列に、表示されます。
この動作を有効にするには、アプリケーションを実行する前にコマンド set ASAN_VCASAN_DEBUGGING=1 を実行します。 強化されたデバッグ エクスペリエンスは、set ASAN_VCASAN_DEBUGGING=0 を実行することで無効にできます。
関連項目
MSVC AddressSanitizer の概要
MSVC AddressSanitizer の既知の問題
MSVC AddressSanitizer ランタイム リファレンス
MSVC AddressSanitizer シャドウ バイト
MSVC AddressSanitizer クラウドまたは分散テスト
MSVC AddressSanitizer デバッガーの統合
MSVC AddressSanitizer エラーの例