次の方法で共有


AddressSanitizer ランタイム

AddressSanitizer ランタイム ライブラリは、メモリ アクセスの検査を有効にするために、一般的なメモリ割り当て関数と操作をインターセプトします。 コンパイラによって生成される可能性のあるさまざまな種類の実行可能ファイルをサポートする、さまざまランタイム ライブラリがあります。 コンパイラとリンカーは、 /fsanitize=address オプションをコンパイル時に渡す限り、適切なランタイム ライブラリを自動的にリンクします。 この既定の動作は、/NODEFAULTLIB オプションをリンク時に使用することでオーバーライドできます。 詳細については、AddressSanitizer 言語、ビルド、デバッグのリファレンスリンクに関するセクションを参照してください。

cl /fsanitize=addressを使用してコンパイルすると、コンパイラはシャドウ バイトを管理およびチェックする命令を生成します。 プログラムは、このインストルメンテーションを使用して、スタック上、ヒープ内、またはグローバル スコープでのメモリ アクセスを調べます。 コンパイラは、スタック変数とグローバル変数を記述するメタデータも生成します。 このメタデータにより、ランタイムは正確なエラー診断 (ソース コード内の関数名、行、列) を生成できるようになります。 コンパイラ チェックとランタイム ライブラリを組み合わせることで、さまざまな種類のメモリ安全性バグを、実行時に発生した場合に、正確に診断できます。

Visual Studio 17.7 Preview 3 以降、AddressSanitizer ランタイムにリンクするためのランタイム ライブラリの一覧を次に示します。 /MT (ランタイムを静的にリンク) および/MD (実行時に再リストを動的にリンクする) オプションの詳細については、「/MD/MT/LD (Run-Time ライブラリを使用する)」を参照してください。

Note

次の表では、 {arch}i386 または x86_64です。 これらのライブラリは、アーキテクチャ名に Clang 規則を使用します。 MSVC 規則は、通常、x86x64ではなく、i386x86_64されますが、同じアーキテクチャを参照します。

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 ランタイム dll がどのようにリンクされているかを示す図。

この図は、ASan ランタイム ライブラリをリンクするための 4 つのシナリオを示しています。 シナリオは、/MT (ランタイムを静的にリンクする)、/MTd (デバッグ ランタイムを静的にリンクする)、/MD (実行時に再リストを動的にリンクする)、/MDd (実行時にデバッグの再リストを動的にリンクする) です。 いずれの場合も、my_exe.exe リンクとその関連付け my_dll.dll、clang_rt.asan_dynamic-x86_64.dllの単一インスタンスへのリンクです。

静的にリンクする場合でも、ASan ランタイム DLL は、他の C ランタイム コンポーネントとは異なり、実行時に存在する必要があります。

以前のバージョン

Visual Studio 17.7 Preview 3 より前では、静的にリンクされた (/MT または /MTd) ビルドでは DLL 依存関係が使用されませんでした。 代わりに、AddressSanitizer ランタイムはユーザーの EXE に静的にリンクされていました。 その後、DLL プロジェクトはユーザーの EXE からエクスポートを読み込んで、ASan 機能にアクセスします。

動的にリンクされたプロジェクト (/MD または /MDd) では、プロジェクトがデバッグ用とリリース用に構成されているかどうかに応じて、異なるライブラリと DLL が使用されていました。 これらの変更とその動機の詳細については、「 MSVC AddressSanitizer – すべてのランタイム構成に対して 1 つの DLL」を参照してください。

次の表では、Visual Studio 17.7 Preview 3 より前の AddressSanitizer ランタイム ライブラリ リンクの以前の動作について説明します。

CRT オプション DLL または EXE DEBUG? ASan ライブラリ (.lib) ASan ランタイム バイナリ (.dll)
/MT EXE No clang_rt.asan-{arch}clang_rt.asan_cxx-{arch} None
/MT DLL No clang_rt.asan_dll_thunk-{arch} None
/MD Either No clang_rt.asan_dynamic-{arch}clang_rt.asan_dynamic_runtime_thunk-{arch} clang_rt.asan_dynamic-{arch}
/MT EXE Yes clang_rt.asan_dbg-{arch}clang_rt.asan_dbg_cxx-{arch} None
/MT DLL Yes clang_rt.asan_dbg_dll_thunk-{arch} None
/MD Either Yes 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 ライブラリがどのようにリンクされていたかを示しています。

Visual Studio 2022 Preview 3 より前に ASan ランタイム dll がどのようにリンクされていたかを示す図。

この図は、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 のインターセプトした関数の一覧」を参照してください。 割り当てインターセプターは、それぞれの割り当て呼び出しに関連するメタデータとシャドウ バイトを管理します。 mallocdelete のような CRT 関数が呼び出されるたびに、インターセプターは特定の値を AddressSanitizer シャドウメモリ領域内に設定して、これらのヒープ位置に現在アクセスできるかどうかと、割り当ての境界は何かを示します。 これらのシャドウ バイトを使用すると、 シャドウ バイト のコンパイラによって生成されたチェックで、読み込みまたはストアが有効かどうかを判断できます。

インターセプトが成功するとは限りません。 関数のプロローグが短すぎて jmp を書き込むことができない場合、インターセプトは失敗する可能性があります。 インターセプトエラーが発生した場合、プログラムはを debugbreak スローして停止します。 デバッガーをアタッチすると、インターセプト問題の原因が明確になります。 この問題が発生した場合は、バグを報告してください。

Note

ユーザーは、必要に応じて、環境変数 ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE を任意の値に設定して、失敗したインターセプトの後も継続しようと試みることができます。 インターセプト エラーの後も継続すると、その関数のバグ レポートが失われる可能性があります。

カスタム アロケーターと AddressSanitizer ランタイム

AddressSanitizer ランタイムは、一般的なアロケーター インターフェイス、malloc/freenew/deleteHeapAlloc/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)

Note

メモリを手動で有害化する場合は、再利用する前にメモリを取り消す必要があります。 これは、プログラムの実行中に頻繁に再利用されるローカル変数など、スタック アドレスで特に重要です。 スタック フレームが削除される前に、手動で有害なスタック アドレスに誤検知 use-after-poison 発生するリスクがあります。

AddressSanitizer ポイズニングのアラインメント要件

シャドウバイトの手動ポイズニングでは、アラインメント要件を考慮する必要があります。 ユーザーはパディングを必要に応じて追加して、シャドウ バイトがシャドウ メモリ内のバイト境界で終了するようにしなければなりません。 AddressSanitizer シャドウ メモリ内の各ビットは、アプリケーションのメモリ内の 1 バイトの状態をエンコードします。 このエンコーディングは、各割り当ての合計サイズ (パディングを含む) を8バイト境界に揃える必要があることを意味します。 アライメント要件が満たされていない場合、バグ報告が正しくなくなる可能性があります。 誤ったレポートは、欠落レポート (偽陽性) として、またはエラーなしのレポート (偽陽性) を示すことがあります。

アラインメントの要件と潜在的な問題の説明は、提供されている ASan アラインメントの例を参照してください。 1 つは、手動シャドウ メモリ ポイズニングで何が問題になるかを示す小さなプログラムです。 2 つ目は、std::allocator インターフェイスを使用し た手動ポイズニングの実装例です。

ランタイム オプション

MSVC AddressSanitizer は、 Clang AddressSanitizer ランタイムの定期的に同期されたフォークです。 その結果、MSVC は Clang の ASan ランタイム オプションの多くを暗黙的に継承します。 積極的に保守およびテストするオプションの完全な一覧については、 こちらをご覧ください。 期待どおりに機能しないオプションが見つかった場合は、バグを報告してください。

ランタイム オプションを構成する

ASan ランタイム オプションは、次の 2 つの方法のいずれかで設定されます。

  • ASAN_OPTIONS 環境変数
  • __asan_default_options ユーザー関数

環境変数とユーザー関数で競合するオプションが指定されている場合は、 ASAN_OPTIONS 環境変数のオプションが優先されます。

複数のオプションは、コロン (:) で区切って指定します。

次の例では、 alloc_dealloc_mismatch を 1 に設定し、 symbolize を 0 に設定します。

set ASAN_OPTIONS=alloc_dealloc_mismatch=1:symbolize=0

または、次の関数をコードに追加します。

extern "C" const char* __asan_default_options()
{
  return "alloc_dealloc_mismatch=1:symbolize=0";
}

サポートされていない AddressSanitizer オプション

  • detect_container_overflow
  • unmap_shadow_on_exit

Note

AddressSanitizer ランタイム オプション halt_on_error は、期待どおりに機能しません。 Clang と MSVC ランタイム ライブラリの両方で、多くのエラーの種類は、ほとんどのメモリ破損エラーを含め、 連続しないと見なされます。

詳細については、「Clang 12.0 との違い」を参照してください。

MSVC 固有の AddressSanitizer ランタイム オプション

  • continue_on_error ブール値。既定で false に設定されます。 trueに設定すると、メモリ違反が報告された後もプログラムの実行を続行でき、複数のエラー レポートを収集できます。

  • iat_overwrite 文字列。既定で "error" に設定されます。 その他の使用可能な値は、 "protect""ignore"です。 一部のモジュールは、特定の関数の実装をカスタマイズするために、他のモジュールの import address table を上書きすることがあります。 たとえば、ドライバーは通常、特定のハードウェアのカスタム実装を提供します。 iat_overwrite オプションは、特定のmemoryapi.h関数の上書きに対する AddressSanitizer ランタイムの保護を管理します。 ランタイムは現在、保護のために VirtualAllocVirtualProtect、および 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_errorwindows_fast_fail_on_errorの両方がtrueされている場合、プログラムは__fastfailで終了します。

  • windows_hook_legacy_allocatorsブール値。falseおよびGlobalAllocアロケーターのインターセプトを無効にするLocalAllocに設定します。

    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

AddressSanitizer のインターセプトした関数の一覧 (Windows)

AddressSanitizer ランタイムは、実行時にメモリの安全性チェックを有効にするために、多くの関数をホットパッチします。 AddressSanitizer ランタイムが監視する関数の包括的でない一覧を次に示します。

既定のインターセプター

省略可能なインターセプター

ここに示すインターセプターがインストールされるのは、AddressSanitizer ランタイム オプションが有効になっているときだけです。 レガシ アロケーターのインターセプトを無効にするには、 windows_hook_legacy_allocatorsfalse に設定します。 set ASAN_OPTIONS=windows_hook_legacy_allocators=false

こちらも参照ください

AddressSanitizer の概要
AddressSanitizer の既知の問題
AddressSanitizer のビルドと言語リファレンス
AddressSanitizer シャドウ バイト
AddressSanitizer クラウドまたは分散テスト
AddressSanitizer デバッガーの統合
AddressSanitizer エラーの例