AddressSanitizer
概要
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 サポートを有効にします。 インストーラーは、Tools>Get Tools and Features... を使用して Visual Studio のメイン メニューから開くことができます。Visual Studio インストーラーから既存の Visual Studio インストールで Modify を選択して、次の画面にアクセスします。
Note
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 -継続的インテグレーション/継続的開発
ファジー処理 - libFuzzer ラッパーを使用したビルド
- Azure OneFuzz
- ローカル コンピューター
この記事では、前述の 3 つのワークフローを有効にするために必要な情報について説明します。 この情報は、AddressSanitizer プラットフォームに依存 Windows 10 の実装に固有です。 このドキュメントは、既に公開されている Google、Apple、および GCC の優れたドキュメントを補足しています。
Note
現在のサポートが 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
、PGO
など) と互換性があります。 このオプションは、静的および動的 CRT (たとえば /MD
、/MDd
、/MT
、/MTd
など) と連動します。 EXE を作成する場合も DLL を作成する場合も機能します。 最良の呼び出し履歴の書式設定をするためにデバッグ情報が必要です。 次の例では、コマンド ラインで cl /fsanitize=address /Zi
が渡されます。
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 の開発者コマンド プロンプトを使用して、main.cpp
のコンパイルを /fsanitize=address /Zi
を使用して行います
結果の main.exe
をコマンド ラインで実行すると、次の書式設定されたエラー レポートが作成されます。
7 つの重要情報を強調表示する、次のような赤色の枠が表示されているとします。
エラー レポートには、重要な情報を識別する 7 つの赤い強調表示があります。 このスクリーンショットに続く番号付きリストにマップされます。 番号付きボックスは、次のテキストを強調表示します。1) グローバル バッファー オーバーフロー 2) サイズ 4 3 の WRITE) basic-global-overflow.cpp 7 4) サイズ 400 6) 00 00[f9]f9 f9 7) ボックスがシャドウ バイト凡例領域にあり、グローバル レッドゾーン f9 で定義されているグローバル変数 basic-global-overflow.cpp 'x' の右側に表示されます。
赤の強調表示 (上から下へ)
- メモリ安全性のバグは、グローバルバッファーオーバーフローです。
- 4 バイト (32 ビット) がユーザー定義変数の外側に格納されています。
- ストアは、ファイル
basic-global-overflow.cpp
の 7 行目で定義されているmain()
関数で行なわれました。 x
という名前の変数は 3 行目の basic-global-overflow.cpp で定義されており、列 8 で開始しています。- このグローバル変数
x
のサイズは 400 バイトです。 - ストアの対象となるアドレスを記述する正確なシャドウ バイトには、
0xf9
の値があります。 - シャドウバイトの凡例は、
0xf9
がint x[100]
の右側のパディングの領域であることを示します。
Note
呼び出し履歴内の関数名は、エラー時にランタイムによって呼び出される LLVM シンボライザーによって生成されます。
Visual Studio で AddressSanitizer を使用する
AddressSanitizer は Visual Studio IDE と統合されています。 MSBuild プロジェクトの AddressSanitizer を有効にするには、ソリューション エクスプローラーでプロジェクトを右クリックし、Properties を選択します。 [プロパティ ページ] ダイアログで、[構成プロパティ]>[C/C++]>[全般] を選択してから、[AddressSanitizer を有効にする] プロパティを変更します。 [OK] を選択して変更を保存します。
IDE からビルドするには、互換性のないオプションをオプトアウトします。 /Od
(またはデバッグ モード) を使用 してコンパイルした既存のプロジェクトの場合は、次のオプションをオフにする必要があります。
- エディット コンティニュをオフにする
/RTC1
(ランタイム チェック)をオフにする/INCREMENTAL
(インクリメンタル リンク)をオフにする
デバッガーをビルドして実行するには、 F5 キーを押します。 Visual Studio に Exception Thrown ウィンドウが表示されます。
Visual Studio から AddressSanitizer を使用する: CMake
Windows をターゲットとして作成された CMake プロジェクトに対して AddressSanitizer を有効にするには次の手順に従います。
IDE の上部にあるツール バーの [構成] ドロップダウンを開いて、[構成の管理] を選択します。
CMake プロジェクト設定エディターが開き、プロジェクトの
CMakeSettings.json
ファイルの内容が反映されます。エディターで [JSON の編集] リンクを選択します。 この選択によって、ビューが未加工の JSON に切り替わります。
"configurePresets":
内の"windows-base"
プリセットに次のスニペットを追加して、Address Sanitizer を有効にします。"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 プロジェクトで既定で有効になっているエディット コンティニュが指定されている (
/ZI
) 場合、アドレスサニタイザーは機能しません。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>>")
Ctrl + S を入力して、この JSON ファイルを保存します。
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-mismatch
allocation-size-too-big
calloc-overflow
double-free
dynamic-stack-buffer-overflow
global-buffer-overflow
heap-buffer-overflow
heap-use-after-free
invalid-allocation-alignment
memcpy-param-overlap
new-delete-type-mismatch
stack-buffer-overflow
stack-buffer-underflow
stack-use-after-return
stack-use-after-scope
strncat-param-overlap
use-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 に関する影響力の大きい論文で、実装について説明しています。
関連項目
AddressSanitizer の既知の問題
AddressSanitizer のビルドと言語リファレンス
AddressSanitizer ランタイム リファレンス
AddressSanitizer シャドウ バイト
AddressSanitizer クラウドまたは分散テスト
AddressSanitizer デバッガーの統合
AddressSanitizer エラーの例