コードサニタイザーは、コンパイラが実行時にインストルメンテーションコードを挿入することで、未定義または疑わしい動作の形でバグを検出するプログラミング ツールです。このクラスのツールは、2012 年に Google のAddressSanitizer (またはASan ) によって初めて導入されました。このツールは、直接マップされたシャドウ メモリを使用して、バッファ オーバーフローやダングリング ポインタへのアクセス(解放後使用) などのメモリ破損を検出します。
アドレスサニタイザー
2012 年に導入された Google の ASan は、シャドウ メモリスキームを使用してメモリ バグを検出します。次のプラットフォームで利用できます。
平均すると、インストルメンテーションにより処理時間が約 73% 増加し、メモリ使用量が 240% 増加します。[5] AArch64 および (限定的に) x86_64 では、HWAsan と呼ばれるハードウェア アクセラレーション ASan が利用可能です。[6]
AddressSanitizerは初期化されていないメモリの読み取りを検出しません(ただし、これはMemorySanitizer [7]によって検出されます)。また、一部のuse-after-returnバグのみを検出します。[8]また、任意のメモリ破損バグや、整数アンダーフロー/オーバーフロー(未定義の動作を持つ整数を使用してメモリアドレスオフセットを計算する場合)による任意の書き込みバグをすべて検出することはできません。構造体とクラス内の隣接するバッファは、下位互換性を壊さないようにするため、オーバーフローから保護されていません。[9]
カーネルアドレスサニタイザー
KernelAddressSanitizer ( KASan )は、Linuxカーネルの動的メモリエラーを検出します。[10]カーネルは通常のプログラムと同じアドレス空間を使用しないため、カーネルインストルメンテーションには、コマンドラインオプションを提供するコンパイラの特別な機能が必要です。[11] [12]-fsanitize=kernel-address
その他の消毒剤
Google は、LeakSanitizer (LSan、メモリ リーク)、ThreadSanitizer (TSan、データ競合とデッドロック)、MemorySanitizer (MSan、初期化されていないメモリ)、UndefinedBehaviorSanitizer ( UBSan、未定義の動作、きめ細かい制御) も開発しました。[13]これらのツールは、Clang/LLVM および GCC で一般的に利用できます。[14] [15] [16] KASan と同様に、LSan、MSan、TSan のカーネル固有バージョンや、KFENCE や KCSan などの完全にオリジナルのカーネル サニタイザーがあります。[17]
追加のサニタイザーツール(-fsanitizeまたは同様のフラグでコンパイラごとにグループ化)には次のものがあります:[14] [15] [16]
- LLVM制御フロー整合性とカーネル対応。仮想テーブルと型キャストをチェックして、前方エッジ CFI を検出します。
- MemTagSanitizerは、Armv8.5-Aの機能を使用してオーバーヘッドを非常に低く抑えるASanのようなツールです。
- ShadowCallStackは、シャドウスタック保護を提供するAArch64ツールです。
- Scudo Hardened Allocatorは、低オーバーヘッドの確率的ASanアナログであるGWP-ASanを含む代替メモリアロケータである[18]
- libFuzzer、ファジングにコードカバレッジを追加するLLVMツール[19]
使用法
コードサニタイザーはプログラム実行中に疑わしい動作を検出します。サニタイザーを使用する一般的な方法の1つは、バグを引き起こす可能性のある入力を生成するファジングと組み合わせることです。[20]
ユーザー
ChromiumとFirefoxの開発者はAddressSanitizerの積極的なユーザーです。[20] [21]このツールはこれらのウェブブラウザで数百のバグを発見しました。[22] FFmpeg [23]とFreeType [24]にも多数のバグが見つかりました。LinuxカーネルはLinuxバージョン4.0以降、 x86-64アーキテクチャでAddressSanitizerを有効にしました。
例
ASan: ヒープ使用後の解放
// コンパイルするには: g++ -O -g -fsanitize=address heap-use-after-free.cc
int main ( int argc 、char ** argv ) {
int *配列=新しいint [ 100 ];
[]配列を削除します。
配列を返す[ argc ]; // BOOM
}
$ ./a.out
==5587==エラー: AddressSanitizer: pc 0x47b55f bp 0x7ffc36b28200 sp 0x7ffc36b281f8 のアドレス 0x61400000fe44 でヒープ使用後の解放が発生しました
0x61400000fe44 スレッド T0 でサイズ 4 の読み取り
#0 メインの /home/test/example_UseAfterFree.cc:5 の 0x47b55e
#1 __libc_start_main の 0x7f15cfe71b14 (/lib64/libc.so.6+0x21b14)
#2 _start 内の 0x47b44c (/root/a.out+0x47b44c)
0x61400000fe44は400バイトの領域[0x61400000fe40、0x61400000ffd0)の4バイト内にあります。
ここでスレッド T0 によって解放されます:
#0 演算子 delete[](void*) 内の 0x465da9 (/root/a.out+0x465da9)
#1 メインの /home/test/example_UseAfterFree.cc:4 の 0x47b529
以前スレッド T0 によってここに割り当てられました:
#0 0x465aa9 演算子 new[](unsigned long) (/root/a.out+0x465aa9)
#1 メインの /home/test/example_UseAfterFree.cc:3 の 0x47b51e
概要: AddressSanitizer: ヒープ使用後解放 /home/test/example_UseAfterFree.cc:5 main
バグのあるアドレスの周囲のシャドウ バイト:
[...]
0x0c287fff9fb0: ファファファファファファファファファファファファファファファファファファファ
=>0x0c287fff9fc0: fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd fd
0x0c287fff9fd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
[...]
シャドウ バイトの凡例 (1 つのシャドウ バイトは 8 つのアプリケーション バイトを表します):
アドレス可能: 00
部分的にアドレス可能: 01 02 03 04 05 06 07
ヒープ左レッドゾーン: fa
ヒープ右レッドゾーン: fb
解放されたヒープ領域: fd
スタック左レッドゾーン: f1
スタックミッドレッドゾーン: f2
スタック右レッドゾーン: f3
スタック部分レッドゾーン: f4
復帰後のスタック: f5
スコープ後のスタック使用: f8
グローバルレッドゾーン: f9
グローバル初期化命令: f6
ユーザーによって毒殺されました: f7
ASan内部: fe
==5587==中止中
ASan: ヒープバッファオーバーフロー
// 実行: clang++ -O -g -fsanitize=address heap-buf-of.cc && ./a.out
int main ( int argc 、char ** argv ) {
int *配列=新しいint [ 100 ];
配列[ 0 ] = 0 ;
int res = array [ argc + 100 ]; // BOOM
[]配列を削除します。
resを返します。
}
==25372==エラー: AddressSanitizer: アドレス 0x61400000ffd4、pc 0x0000004ddb59 bp 0x7fffea6005a0 sp 0x7fffea600598 でヒープ バッファ オーバーフローが発生しました
0x61400000ffd4 スレッド T0 でサイズ 4 の読み取り
#0 メインの /tmp/main.cpp:4:13 の 0x46bfee
0x61400000ffd4は、400バイト領域[0x61400000fe40、0x61400000ffd0)の右4バイトに位置します。
ここでスレッド T0 によって割り当てられます:
#0 演算子 delete[](void*) 内の 0x4536e1
#1 メインの /tmp/main.cpp:2:16 の 0x46bfb9
ASan: スタックバッファオーバーフロー
// 実行: clang -O -g -fsanitize=address stack-buf-of.cc && ./a.out
int main ( int argc 、char ** argv ) {
整数スタック配列[ 100 ];
スタック配列[ 1 ] = 0 ;
return stack_array [ argc + 100 ]; // BOOM
}
==7405==エラー: AddressSanitizer: アドレス 0x7fff64740634、pc 0x46c103、bp 0x7fff64740470、sp 0x7fff64740468 でスタック バッファ オーバーフローが発生しました
0x7fff64740634 スレッド T0 でサイズ 4 の読み取り
#0 メインの /tmp/example_StackOutOfBounds.cc:5 の 0x46c102
アドレス0x7fff64740634は、フレーム内のオフセット436のスレッドT0のスタックにあります。
#0 メインの /tmp/example_StackOutOfBounds.cc:2 の 0x46bfaf
このフレームには 1 つのオブジェクトがあります:
[32, 432) 'stack_array' <== オフセット436でのメモリアクセスがこの変数をオーバーフローします
ASan: グローバルバッファオーバーフロー
// 実行: clang -O -g -fsanitize=address global-buf-of.cc && ./a.out
int global_array [ 100 ] = { -1 }; int main ( int argc , char ** argv ) { return global_array [ argc + 100 ]; // BOOM }
==7455==エラー: AddressSanitizer: アドレス 0x000000689b54、pc 0x46bfd8、bp 0x7fff515e5ba0、sp 0x7fff515e5b98 でグローバル バッファ オーバーフローが発生しました
0x000000689b54 スレッド T0 でサイズ 4 の読み取り
メインの /tmp/example_GlobalOutOfBounds.cc:4 の #0 0x46bfd7
0x000000689b54は、4バイト右に位置します。
サイズ 400 の 'example_GlobalOutOfBounds.cc' (0x6899c0) からのグローバル変数 'global_array'
UBSan: nullptr 参照解除
// 実行: g++ -O -g -fsanitize=null null-dereference.c && ./a.out
int main ( int argc , char ** argv ) { const char * ptr = nullptr ; return * ptr ; // BOOM }
null-dereference.c:4:10: ランタイム エラー: 'const char' 型の null ポインタのロード セグメンテーション違反(コアダンプ)
参照
- インテル MPX
- Microsoft Windows SDKのアプリケーション検証ツール (AppVerif.exe)
- メモリデバッグツールValgrind
参考文献
- ^ 「LLVM 3.1 リリースノート」。LLVM 。 2014年2月8日閲覧。
- ^ 「GCC 4.8 リリースノート」。GCC 。 2014年2月8日閲覧。
- ^ 「アドレスサニタイザー | Apple 開発者向けドキュメント」。
- ^ 「Visual Studio 2019 バージョン 16.9 リリース ノート」。Microsoft。2021年3 月 5 日閲覧。
- ^ Konstantin Serebryany、Derek Bruening、Alexander Potapenko、Dmitry Vyukov 。「AddressSanitizer: 高速アドレスサニティチェッカー」(PDF) 。2012 USENIX カンファレンスの議事録。年次技術カンファレンス。
- ^ 「ハードウェア支援 AddressSanitizer 設計ドキュメント — Clang 17.0.0git ドキュメント」。clang.llvm.org。
- ^ 「MemorySanitizer」。GitHub。
- ^ 「ComparisonOfMemoryTools」。AddressSanitizer Wiki 。 2017年12月1日閲覧。
- ^ 「Bypassing AddressSanitizer」(PDF) Eric Wimberley 。 2014年7月1日閲覧。
- ^ 「KernelAddressSanitizer (KASAN)」。2015年9月15日時点のオリジナルよりアーカイブ。
- ^ Jake Edge. 「カーネル アドレス サニタイザー」
- ^ Jonathan Corbet. 「3.20 マージウィンドウ パート 2」。
- ^ Google (2023年3月2日). 「sanitizers: このプロジェクトは、Sanitizers: AddressSanitizer、MemorySanitizer、ThreadSanitizer、LeakSanitizer などのホームです」. GitHub . Google.
{{cite web}}:|last1=一般的な名前があります (ヘルプ) - ^ ab "sanitizer - The Rust Unstable Book". doc.rust-lang.orgこの
機能により、次のいずれかのサニタイザーの使用が可能になります: [...] ControlFlowIntegrity LLVM 制御フロー整合性
- ^ ab "Clang コンパイラ ユーザーズ マニュアル — Clang 17.0.0git ドキュメント". clang.llvm.org .
-f[no-]sanitize=check1,check2,... さまざまな形式の未定義または疑わしい動作の実行時チェックをオンにします
- ^ ab 「インストルメンテーション オプション (GNU コンパイラ コレクション (GCC) の使用)」。gcc.gnu.org。
- ^ 「Linuxカーネルサニタイザー」。Google。2023年3月2日。
- ^ 「GWP-ASan — LLVM 17.0.0git ドキュメント」。llvm.org。
- ^ 「libFuzzer – カバレッジ ガイド付きファズ テスト用のライブラリ。 — LLVM 17.0.0git ドキュメント」。llvm.org。
- ^ ab Abhishek Arya、Cris Neckar、Chrome セキュリティ チーム。「セキュリティのためのファジング」。
- ^ 「Firefox のセキュリティ確保: 新しいコード分析手法の試用」。2016 年 3 月 7 日時点のオリジナルよりアーカイブ。2018 年 6 月 18 日閲覧。
- ^ 「AddressSanitizer によって発見されたバグの一部」。GitHub。
- ^ Mateusz Jurczyk、Gynvael Coldwind (2014-01-10)。「FFmpeg と 1000 の修正」。J00Ru //Vx Tech Blog。
- ^ 「FreeType Bugs における AddressSanitizer の検索結果」。
外部リンク
- AddressSanitizer Google グループ (メーリング リストなし)
- AddressSanitizer プロジェクトページ
- AddressSanitizer ドキュメント (Clang)
