概要
C および C++ 言語は強力ですが、プログラムの正確性とプログラムのセキュリティに影響を与えるバグのクラスに苦しむ可能性があります。 Visual Studio 2019 バージョン 16.9 以降、Microsoft C/C++ コンパイラ (MSVC) と IDE では、AddressSanitizer サニタイザーがサポートされています。 AddressSanitizer (ASan) は、擬陽性がゼロの数多くの発見しにくいバグを明らかにする、コンパイラおよびランタイム テクノロジです。
-
Alloc/dealloc 不一致と
new/delete型の不一致 - 割り当てがヒープに対して大きすぎる
-
callocオーバーフローとallocaオーバーフロー - 二重解放と解放後の使用
- グローバル変数オーバーフロー
- ヒープバッファーオーバーフロー
- アラインされた値のアラインメントが無効
-
memcpyおよびstrncatパラメーターのオーバーラップ - スタック バッファーのオーバーフローとアンダーフロー
-
return後のスタック使用とスコープ後の使用 - ポイズニングされた後のメモリの使用
AddressSanitizer を使用して、以下にかかる時間を短くします。
- 基本正確性
- クロスプラットフォームの移植性
- セキュリティ
- ストレス テスト
- 新しいコードの統合
AddressSanitizer は、もともと Google によって導入、既存のビルド システムと既存のテスト資産を直接使用するランタイム バグ検出テクノロジを提供します。
AddressSanitizer は、Visual Studio プロジェクト システム、CMake ビルド システム、IDE と統合されています。 プロジェクトで AddressSanitizer を有効にするには、プロジェクトのプロパティを設定するか、ひとつの追加コンパイラ オプション /fsanitize=address を使用します。 新しいオプションは、x86 および x64 のすべてのレベルの最適化および構成と互換性があります。 ただし、 edit-and-continue、 incremental linking、および /RTC と互換性がありません。
Visual Studio 2019 バージョン16.9 以降では、Microsoft の AddressSanitizer テクノロジにより Visual Studio IDE との統合が可能になります。 この機能により、サニタイザーが実行時にバグを発見したとき、必要に応じてクラッシュ ダンプ ファイルを作成できます。 プログラムを実行する前に ASAN_SAVE_DUMPS=MyFileName.dmp 環境変数を設定すると、正確に診断されたバグの 事後分析デバッグ の効率的なメタデータを含むクラッシュ ダンプ ファイルが作成されます。 これらのダンプ ファイルを使用すると、AddressSanitizer を次の目的で簡単に使用できます。
- ローカル コンピューターのテスト
- オンプレミスの分散テスト
- テスト用のクラウドベースのワークフロー
AddressSanitizer のインストール
Visual Studio インストーラーの C++ ワークロードでは、AddressSanitizer ライブラリと IDE 統合が既定でインストールされます。 ただし、以前のバージョンの Visual Studio 2019 からアップグレードする場合は、アップグレード後にインストーラーを使用して ASan サポートを有効にします。 Visual Studioのメイン メニューからインストーラーを開くには、Tools>Get Tools and Features を選択します。 Visual Studio インストーラーから既存のVisual Studio インストールModify を選択して、次の画面にアクセスします。
注記
新しい更新プログラムでVisual Studioを実行しても、ASan をインストールしていない場合は、コードの実行時にエラーが発生します。
LNK1356: ライブラリ 'clang_rt.asan_dynamic-i386.lib' が見つかりません
AddressSanitizer を使用する
次の一般的な開発方法のいずれかを使用して、/fsanitize=address コンパイラ オプションを使用した実行可能ファイルのビルドを開始します。
- コマンドライン ビルド
- Visual Studio のプロジェクト システム
- Visual Studio CMake 統合
再コンパイルしてから、プログラムを通常どおり実行します。 このコード生成では、さまざまな種類の正確に診断されたバグが公開されます。 これらのエラーは、デバッガー IDE で、あるいはコマンドラインで報告されるか、あるいは新しい種類のダンプ ファイルに格納されて正確なオフライン処理が行なわれます。
Microsoft では、次の 3 つの標準ワークフローで AddressSanitizer を使用することをお勧めします。
開発者の内側ループ
- Visual Studio - コマンド ライン
- Visual Studio - プロジェクト システム
- Visual Studio - CMake
CI/CD -継続的インテグレーション/継続的開発
- エラー報告 - 新しい AddressSanitizer ダンプ ファイル
ファジング - libFuzzer ラッパーを使用したビルド作成
- Azure OneFuzz
- ローカル コンピューター
この記事では、前述の 3 つのワークフローを有効にするために必要な情報について説明します。 この情報は、AddressSanitizer の platform 依存 Windows 10 (以降) の実装に固有です。 このドキュメントは、既に公開されている Google、Apple、および GCC の優れたドキュメントを補足しています。
注記
サポートは、Windows 10 以降では x86 と x64 に制限されています。 今後のリリースで期待することについて、フィードバックをお送りください。 お送りいただいたフィードバックは、今後の他のサニタイザー (/fsanitize=thread、/fsanitize=leak、/fsanitize=memory、/fsanitize=undefined、/fsanitize=hwaddress など) の優先順位を決めるのに役立ちます。 問題が発生した場合は、バグをこちらで報告できます。
開発者コマンド プロンプトから AddressSanitizer を使用する
/fsanitize=address コンパイラ オプションを開発者コマンド プロンプトで使用して、AddressSanitizer ランタイムのコンパイルを有効にします。
/fsanitize=address オプションは、既存の C++ または C 最適化レベル (/Od、/O1、/O2、/O2 /GLなど) と互換性があります。 このオプションは、静的および動的 CRT (たとえば /MD、/MDd、/MT、/MTd など) と連動します。 EXE を作成する場合も DLL を作成する場合も機能します。 デバッグ情報はコールスタックの最適な書式設定に必要です。 次の例では、コマンド ラインで cl /fsanitize=address /Zi が渡されます。
注記
AddressSanitizer では、プロファイルガイド付き最適化 (PGO) はサポートされていません。 AddressSanitizer は運用環境では使用しないでください。
AddressSanitizer ライブラリ (.lib ファイル) は自動的にリンクされます。 詳細については、「AddressSanitizer 言語、ビルド、およびデバッグのリファレンス」をご覧ください。
例 - 基本グローバル バッファー オーバーフロー
// basic-global-overflow.cpp
#include <stdio.h>
int x[100];
int main() {
printf("Hello!\n");
x[100] = 5; // Boom!
return 0;
}
Visual Studio 2019 の開発者コマンド プロンプトで、/fsanitize=address /Zi を使用して basic-global-overflow.cpp をコンパイルします。
結果の basic-global-overflow.exe をコマンド ラインで実行すると、次の書式設定されたエラー レポートが作成されます。
7 つの重要情報を強調表示する、次のような赤色の枠が表示されているとします。
赤の強調表示 (上から下へ)
- メモリ安全性のバグは、グローバルバッファーオーバーフローです。
- 4 バイト (32 ビット) がユーザー定義変数の外側に格納されています。
- ファイル
main()の 7 行目で定義されている関数basic-global-overflow.cppで格納されました。 -
xという名前の変数は、列 8 から始まる 3 行目のbasic-global-overflow.cppで定義されます。 - このグローバル変数
xサイズは 400 バイトです。 - ストアの対象となるアドレスを記述する 正確なシャドウ バイト には、
0xf9の値がありました。 - シャドウ バイトの凡例には、
0xf9はint x[100]の右側のパディング領域であると表示されます。
注記
AddressSanitizer がエラーを報告すると、ASan ランタイムによって LLVM シンボル表示が 呼び出され、呼び出し履歴に関数名が生成されます。
Visual Studio で AddressSanitizer を使用する
AddressSanitizer は Visual Studio IDE と統合されています。 MSBuild プロジェクトの AddressSanitizer を有効にするには、ソリューション エクスプローラーでプロジェクトを右クリックし、Properties を選択します。 [プロパティ ページ] ダイアログで、[構成プロパティ]>[C/C++]>[全般] を選択してから、[AddressSanitizer を有効にする] プロパティを変更します。 [OK] を選択して変更を保存します。
IDE からビルドするには、互換性のないオプションをオプトアウトします。
/Od (またはデバッグ モード) を使用してコンパイルされた既存のプロジェクトでは、次のオプションをオフにする必要がある場合があります。
-
/ZIをオフにする -
/RTC1(ランタイム チェック)をオフにする -
/INCREMENTAL(インクリメンタル リンク)をオフにする
デバッガーをビルドして実行するには、 F5 キーを押します。 Visual Studio に Exception Thrown ウィンドウが表示されます。
Visual Studio から AddressSanitizer を使用する: CMake
Windows をターゲットとして作成された CMake プロジェクトに対して AddressSanitizer を有効にするには次の手順に従います。
IDE の上部にあるツール バーの [構成 ] ドロップダウン メニューを開き、[ 構成の管理] を選択します。
この操作により、CMake Project設定エディターが開き、projectの
CMakeSettings.jsonファイルの内容が反映されます。[ JSON の編集] リンクを選択します。 この選択によって、ビューが未加工の JSON に切り替わります。
AddressSanitizer を有効にするには、
"windows-base"内の"configurePresets":プリセットに次のスニペットを追加します。"environment": { "CFLAGS": "/fsanitize=address", "CXXFLAGS": "/fsanitize=address" }"configurePresets"その後、次のようになります。"configurePresets": [ { "name": "windows-base", "hidden": true, "generator": "Ninja", "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { "CMAKE_C_COMPILER": "cl.exe", "CMAKE_CXX_COMPILER": "cl.exe" }, "condition": { "type": "equals", "lhs": "${hostSystemName}", "rhs": "Windows" }, "environment": { "CFLAGS": "/fsanitize=address", "CXXFLAGS": "/fsanitize=address" } },新しい CMake プロジェクトにデフォルトで有効となっている「Edit and Continue」 (
/ZI) が指定されている場合、AddressSanitizer は動作しません。CMakeLists.txtで、#で始まる行をコメント アウト (プレフィックスにset(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT") します。 その行は次のようになります。# set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")この JSON ファイルを保存するには、Ctrl+キーを使用します。
CMake キャッシュ ディレクトリをクリアし、Visual Studio メニューから [ Project>Delete cache and Reconfigure を選択して再構成します。 キャッシュ ディレクトリをクリアして再構成するように求めるプロンプトが表示されたら、[ Yes を選択します。
ソース ファイルの内容 (たとえば、
CMakeProject1.cpp) を次のコードに置き換えます。// CMakeProject1.cpp : Defines the entry point for the application #include <stdio.h> int x[100]; int main() { printf("Hello!\n"); x[100] = 5; // Boom! return 0; }F5 を選択して、デバッガーで再コンパイルして実行します。
このスクリーンショットは、CMake ビルドからのエラーをキャプチャします。
AddressSanitizer「クラッシュ」「ダンプ」
AddressSanitizer では、クラウドおよび分散ワークフローで使用するための新機能が導入されました。 この機能により、IDE で AddressSanitizer エラーをオフラインで表示できます。 エラーはソースの上に重なって表示されるので、ライブのデバッグ セッションで体験するのと同じように見えます。
これらの新しいダンプ ファイルは、バグを分析するときに効率を上げる可能性があります。 リモート データを再実行したり探したり、オフラインになったコンピューターを探したりする必要がなくなります。
別のコンピューターの Visual Studio で後で表示できる新しい種類のダンプ ファイルを生成するには:
set ASAN_SAVE_DUMPS=MyFileName.dmp
Visual Studio 16.9 以降では、 ファイルに格納されている*.dmpをソース コードの上に表示できます。
この新しいクラッシュ ダンプ機能により、 クラウドベースのワークフロー、または分散テストが可能になります。 また、任意のシナリオで詳細でアクション可能なバグを報告するためにも使用できます。
エラーの例
AddressSanitizer は、さまざまな種類のメモリ誤用エラーを検出できます。 AddressSanitizer (/fsanitize=address) コンパイラ オプションを使用してコンパイルされたバイナリを実行するときに報告される数多くのランタイム エラーを次に示します。
alloc-dealloc-mismatchallocation-size-too-bigcalloc-overflowdouble-freedynamic-stack-buffer-overflowglobal-buffer-overflowheap-buffer-overflowheap-use-after-freeinvalid-allocation-alignmentmemcpy-param-overlapnew-delete-type-mismatchstack-buffer-overflowstack-buffer-underflowstack-use-after-returnstack-use-after-scopestrncat-param-overlapuse-after-poison
例の詳細については、「AddressSanitizer エラーの例」を参照してください。
Clang 12.0 との違い
MSVC は現在 Clang 12.0 と、次の 2 つの機能領域で異なります。
- stack-use-after-scope: この設定は既定でオンになっているので、オフにすることはできません。
-
stack-use-after-return: この機能には追加のコンパイラ オプションが必要であり、
ASAN_OPTIONSを設定するだけでは使用できません。
これらの決定は、この最初のバージョンを提供するために必要なテスト マトリックスを減らすために行いました。
Visual Studio 2019 16.9 で誤検知につながる可能性がある機能は含まれませんでした。 その分野では、数十年もの既存のコードとの相互運用を検討するときに必要な効果的なテストの整合性が適用されました。 以降のリリースでは、その他の機能が考慮される可能性があります。
詳細については、「 Building for AddressSanitizer with MSVCを参照してください。
既存の業界ドキュメント
AddressSanitizer テクノロジのこれらの言語とプラットフォーム依存型実装については、広範なドキュメントが既に存在します。
AddressSanitizer (外部)に関するこの資料では、実装について説明しています。