サニタイザーのサポート
rustc コンパイラは、以下のサニタイザーのサポートを含んでいます。
- AddressSanitizer は、より高速なメモリエラー検出器です。 ヒープ、スタック、グローバルへの範囲外アクセス、解放後使用、return 後使用、二重解放、不正な解放、メモリリークを検出できます。
- ControlFlowIntegrity LLVM Control Flow Integrity (CFI) は、フォワードエッジの制御フロー保護を提供します。
- Hardware-assisted AddressSanitizer は、AddressSanitizer に似ていますが、部分的なハードウェア支援に基づくツールです。
- KernelControlFlowIntegrity LLVM Kernel Control Flow Integrity (KCFI) は、オペレーティングシステムのカーネル向けにフォワードエッジの制御フロー保護を提供します。
- LeakSanitizer は、実行時メモリリーク検出器です。
- MemorySanitizer は、未初期化読み取りの検出器です。
- ThreadSanitizer は、高速なデータ競合検出器です。
サニタイザーの使い方
サニタイザーを有効にするには、-Z sanitizer=... オプションを付けてコンパイルします。ここで値は address、cfi、hwaddress、kcfi、leak、memory、thread のいずれかです。
サニタイザーの使用方法の詳細については、
The Unstable Book の sanitizer フラグを参照してください。
rustc ではサニタイザーはどのように実装されているか
サニタイザー(CFI を除く)の実装は、ほぼ完全に LLVM に依存しています。 rustc は LLVM のコンパイル時インストルメンテーションパスとランタイムライブラリの統合ポイントです。 実装における最も重要な側面の要点:
-
サニタイザーランタイムライブラリは compiler-rt プロジェクトの一部であり、
bootstrap.tomlで有効にされている場合、サポート対象ターゲット 上でビルドされます。build.sanitizers = trueランタイムはターゲット libdir に配置されます。
-
LLVM コード生成中に、インストルメンテーション対象の関数には、適切な LLVM 属性がマークされます:
SanitizeAddress、SanitizeHWAddress、SanitizeMemory、またはSanitizeThread。 デフォルトでは、すべての関数がインストルメントされますが、この挙動は#[sanitize(xyz = "on|off|<other>")]で変更できます。 -
インストルメンテーションを実行するかどうかの判断は、関数単位でのみ可能です。 それらの判断が関数間で異なる場合、MIR レベルと LLVM レベルの両方でインライン化を抑制する必要があるかもしれません。
-
rustc によって生成された LLVM IR は、各サニタイザーごとに異なる専用の LLVM パスによってインストルメントされます。 インストルメンテーションパスは、最適化パスの後に呼び出されます。
-
実行可能ファイルを生成する際には、サニタイザー固有のランタイムライブラリがリンクされます。 ライブラリはターゲット libdir 内で検索されます。 まず、検索はオーバーライドされたシステムルートからの相対で行われ、その後、デフォルトのシステムルートからの相対で行われます。 デフォルトのシステムルートへのフォールバックにより、cargo
-Z build-stdや xargo によって構築された sysroot オーバーライドを使用する場合でも、サニタイザーランタイムが引き続き利用可能であることが保証されます。
サニタイザーのテスト
サニタイザーは、tests/codegen-llvm/sanitize*.rs のコード生成テストと、tests/ui/sanitizer/ ディレクトリ内のエンドツーエンドの機能テストによって検証されます。
サニタイザー機能のテストには、サニタイザーランタイム(bootstrap.toml で build.sanitizer = true の場合にビルドされる)と、特定のサニタイザーのサポートを提供するターゲットが必要です。
指定されたターゲットでサニタイザーがサポートされていない場合、サニタイザーのテストは無視されます。
この挙動は、compiletest の needs-sanitizer-* ディレクティブによって制御されます。
新しいターゲットでサニタイザーを有効にする
LLVM によってすでにサポートされている新しいターゲットでサニタイザーを有効にするには:
- ターゲット定義内の
supported_sanitizersのリストにサニタイザーを含めます。rustc --target .. -Zsanitizer=..は、そのサニタイザーをサポート対象として認識するようになるはずです。 - ターゲット用のランタイムをビルドし、それを libdir に含めます。
- そのターゲットがサニタイザーをサポートするようになったことを compiletest に教えます。
needs-sanitizer-*でマークされたテストが、そのターゲット上で実行されるようになるはずです。 - テスト
./x test --force-rerun tests/ui/sanitize/を実行して検証します。 - リリースプロセスの一部としてサニタイザーランタイムをビルドして配布するため、CI 設定で –enable-sanitizers を指定します。