制御フローの整合性に関する技術的な詳細
「カーネル内で膨大な数の関数ポインターが利用できることが、この攻撃パターンの人気を高めています。 攻撃者が独自の実行可能コードを挿入できない場合でも、既存のカーネル コードの任意の部分を実行して悪用を完了することができます。
LLVMの CFI は、有効な呼び出しターゲットを制限し、CFI 違反を検出したときにカーネル パニックを強制することで、これらの攻撃を軽減しようとします。 各間接分岐の前にチェックが追加され、ターゲット アドレスが正しい署名を持つ有効な関数を指していることを確認します。 これにより、間接分岐が任意のコードの場所にジャンプすることがなくなり、呼び出せる関数も制限されます。 バグによってアクセスが許可された場合でも、攻撃者は関数ポインターを変更することができます。 しかし、LLVM の CFI は、間接呼び出しの 55% を最大 5 つのターゲットに制限し、80% を最大 20 のターゲットに制限します。 各間接分岐の有効な呼び出しターゲットをすべて決定するために、コンパイラーはすべてのカーネル コードを一度に確認する必要があります。
LTOの使い方(リンク時間の最適化)これを可能にします。 LLVM の CFI では、コンパイラがすべての C 言語に対して LLVM 固有のビットコードを生成する LTO の使用が必要です。 コンパイル ユニットと、LTO 対応リンカーが LLVM バックエンドを使用してビットコードを結合し、それをコンパイルします。 ネイティブコード。
CFI の使用を許可することに加えて、LTO はプログラム全体の分析とモジュール間の最適化を通じて、より優れたランタイム パフォーマンスを実現します。
ThinLTO LTO のパフォーマンス向上にほぼ追いつきました。 ThinLTOモードでは、通常のLTOと同様に、 クラン コンパイルフェーズの後に LLVM ビットコードを生成します。 ThinLTO ビットコードは、モジュールのコンパクトな概要で強化されています。 リンク ステップでは、概要のみが読み取られて、結合された概要インデックスにマージされます。これには、後でモジュール間関数をインポートするための関数の場所のインデックスが含まれます。 その後、結合されたサマリー インデックスに対して高速かつ効率的なプログラム全体の分析が実行されます。 ThinLTO ではマルチスレッドのリンク プロセスが可能になり、コンパイル時間が短縮されます。
CFI は、特定のバグ クラスに遭遇するとプログラムの実行を中断するため、許容モードで使用すると、前述したようにバグ発見ツールとしても分類されます。 Permissive CFI は、カーネル パニックを強制せずに、CFI 違反をカーネル ログに表示します。 コア 4.9 (Pixel 3 世代のデバイス) および 4.14 (Pixel 4 世代のデバイス) カーネルには、いくつかの関数タイプがありました。 CFI 違反を引き起こす不一致。これは、カーネル/共通で利用可能なパッチセットで Google によって対処されました。 リポジトリ。
ただし、Android エコシステムの性質により、これらの不一致は SoC メーカー (この場合は Qualcomm) または OEM (OnePlus) 固有のコードでも見つかる可能性があります。 4.19 カーネルとは異なる Qualcomm コードのいくつかの CFI 違反が、OnePlus 8 Pro の Kirisakura カーネルで修正されました (例: 1, 2, 3).
寛容な CFI でカーネルを実行すると、OnePlus ドライバーに関連するコードでも CFI 違反が明らかになりました (関連するコミットが見つかります) ここ そして ここ). OnePlus 8 Pro の霧桜カーネルは CFI を適用して実行され、この種のコード再利用攻撃からユーザーを保護します。」
続きを読む
DIY 愛好家 (古い PC パーツのサルベージャーなど)。 Eclair 時代からの Android の熱心なユーザーである Skanda は、シングルボード コンピューティングの世界における最近の開発トレンドを追うことも好きです。