Address Sanitizerとは
Address Sanitizer(ASan)とは,配列外参照やUse-After-Freeのようなメモリ周りの実行時エラーを検出してくれるC/C++向けのランタイム機構です.
本来のC/C++にはこういった機構はなく,配列境界を2,3個超え踏み抜いたところでそのまま実行が続いてしまい,メモリ上の隣接するアドレスの変数の値を読み書きしてしまうことがあります.
そこでコンパイル時に,アドレスのアクセス可否情報を表すデータ構造とそのデータ構造を使ってメモリアクセス時にアドレスをチェックするコードを埋め込み,実行時にメモリアクセスの可否をチェックできるようにしたものがAddress Sanitizerです.
これのおかげでC/C++プログラムの動的デバッグがかなり簡単になります.
詳しい原理は以下のページが詳しいです.
本題
便利なAddress Sanitizerですが,Windowsで利用するには以下の2通りがあります.
- Visual StudioのC/C++ワークロード
- デフォルトでAddress Sanitizerが同梱されているようです
- 参考 AddressSanitizer | Microsoft Learn
- プロプライエタリ(無料版あり)
- ストレージ消費量大き目
-
LLVM MinGW
- LLVMをベースにしたMinGW-w64ツールチェーン
- Address Sanitizerも利用可能
- 無料
- ストレージ消費量小さ目(500~800MB程度)
IDEのVisual Studioが不要であったり,あるいは無料で使えるコンパクトな環境が欲しければLLVM MinGWを使うのがいいでしょう.
Windows 11(24H2)での問題
LLVM MinGWをWindows 11(24H2)使い,C/C++プログラムをAddress Sanitizer付でコンパイルすると,プログラムの起動後,以下のような表示が出て異常終了します.
なお,確認したときのバージョンはLLVM MinGW 19.1.6です.
本来であれば
hello, world
と表示されますが,ここでは
==54756==interception_win: unhandled instruction at 0x7ffa31bf31d0: 44 0f b6 1a 4c 8b d2 48
==54756==interception_win: unhandled instruction at 0x7ffa31bf31d0: 44 0f b6 1a 4c 8b d2 48
と表示されます.
これはプログラム+プログラムにリンクされた動的ライブラリ内の命令の中にAddress Sanitizerが対応していない命令があるということです.
もう少し具体的に書くと,x86_64の命令セットのオペコードは固定長ではありません.
バイト列から命令のサイズを返す関数を用意して,Address Sanitizerがプログラムのバイト列を解析する際に使用しています.
この関数は interception_win.cppで実装されています 1 .
以下が当該関数の冒頭部分のキャプチャです.
勘のいいひとはこれで気づくかもしれませんが,片っ端からオペコードをハードコードしてマッチングしています.
本題に戻ると,Windows11 24H2では「44 0f b6 1a 4c 8b d2 48
」というオペコードが対応していないようです.
このファイル内で調べてみましょう.
完全一致はしなかったものの,先頭4バイト部分にヒットする箇所がありました.
これは4バイトと認識できているようです.
これはmainブランチの最新コミットなので修正されているようですが2,LLVM 19.x系列ではこの修正はとりこまれていません.
したがって,現行の最新バージョンが19.x系列であれば,前掲のエラーは発生します.
対処法
対処法は2通りあります.
- LLVM 20.xがリリースされるのを待つ
- LLVMの公式ページによれば,LLVM 20.xがリリースされるのは3/11の予定
- プレリリースバージョンを使う
- LLVM 20.1.0のプレリリースバージョンはすでに出ています.
- LLVM MinGWは公式のLLVMリポジトリに合わせてリリースされており,プレリリース版も出ている
後者のプレリリースバージョンですが,筆者環境で正常に動作し,Address Sanitizer付でコンパイルしたプログラムも無事に動作しました.