AArch64 バイナリ用の実行専用メモリ(XOM)

AArch64 システム バイナリの実行可能コード セクションは、ジャストインタイム コード再利用攻撃に対する強化対応策として、デフォルトで「実行専用」(読み取り不可)とマークされています。データとコードを混在させたコード、また(メモリ セグメントを最初に読み取り可能として再マップせず)意図的にこれらのセクションを検査するコードは、機能しなくなりました。Android 10(API レベル 29 以降)のターゲット SDK を使用したアプリは、メモリ内の実行専用メモリ(XOM)対応システム ライブラリのコード セクションを、アプリが最初に読み取り可能としてマークせずに読み込もうとした場合に影響を受けます。

この対応策を最大限に活用するには、ハードウェアとカーネルの両方のサポートが必要です。このサポートがなければ、対応策は部分的にしか実施されない可能性があります。Android 4.9 共通カーネルには、ARMv8.2 デバイスでこの対応策を完全にサポートするための適切なパッチが含まれています。

実装

コンパイラが生成する AArch64 バイナリは、コードとデータが混在していないことを前提としています。この機能を有効にしても、デバイスのパフォーマンスに悪影響はありません。

実行可能セグメントで意図的なメモリ イントロスペクションを行わなければならないコードの場合、検査を必要とするコードのセグメントに対して mprotect を呼び出して読み取り可能にし、検査が完了したら読み取り可能権限を削除することをおすすめします。
この実装では、実行専用としてマークされたメモリ セグメントへの読み取りが行われ、セグメンテーション違反(SEGFAULT)が発生します。これは、バグ、脆弱性、コードが混在したデータ(リテラル プーリング)、意図的なメモリ イントロスペクションの結果として発生する可能性があります。

デバイスのサポートと影響

必要なパッチが適用されていない、以前のハードウェアまたは以前のカーネル(4.9 より前)のデバイスでは、この機能が完全にサポートされない、またはこの機能のメリットが得られない可能性があります。カーネル サポートがないデバイスでは、実行専用メモリのユーザー アクセスを適用できないかもしれませんが、ページが読み取り可能かどうかを明示的に調べるカーネルコードであれば、このアクセス特性を適用できる可能性はあります(process_vm_readv() など)。

実行専用としてマークされたユーザーランドのページにカーネルが配慮するように、カーネルにカーネルフラグ CONFIG_ARM64_UAO を設定する必要があります。以前の ARMv8 デバイス、またはユーザー アクセス オーバーライド(UAO)が無効になっている ARMv8.2 デバイスは、この機能のメリットを完全には活かせない可能性がありますが、syscall を使用すれば実行専用ページを読み取れる可能性があります。

既存のコードのリファクタリング

AArch32 から移植されたコードには、データとコードが混在しており、問題が発生する可能性があります。多くの場合、この問題は定数をアセンブリ ファイルの .data セクションに移動するだけで解決できます。

ローカルにプールされた定数を分離するには、手書きのアセンブリをリファクタリングすることが必要な場合があります。

例:

Clang コンパイラで生成されるバイナリは、コード内にデータが混在しても問題はありません。GNU コンパイラ コレクション(GCC)で生成されるコードが(静的ライブラリから)含まれている場合、出力バイナリを検査して、定数がコード セクションにプールされていないことを確認します。

実行可能コード セクションでコード イントロスペクションが必要な場合は、まず mprotect を呼び出してコードを読み取り可能としてマークします。次に、オペレーションが完了したら、再度 mprotect を呼び出して読み取り不可としてマークします。

XOM の有効化

ビルドシステムのすべての 64 ビットバイナリで、実行専用がデフォルトで有効になっています。

XOM の無効化

モジュール レベル、サブディレクトリ ツリー全体、またはビルド全体でグローバルに、実行専用を無効にできます。

LOCAL_XOM 変数と xom 変数を false に設定することで、リファクタリングできないモジュール、または実行可能コードを読み取る必要があるモジュールごとに、XOM を無効にできます。

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

実行専用メモリが静的ライブラリで無効になっている場合、ビルドシステムはその静的ライブラリのすべての依存モジュールにこれを適用します。これは xom: true, を使用することでオーバーライドできます。

特定のサブディレクトリ(例: foo/bar/)内の実行専用メモリを無効にするには、値を XOM_EXCLUDE_PATHS に渡します。

make -j XOM_EXCLUDE_PATHS=foo/bar

または、プロダクト構成で PRODUCT_XOM_EXCLUDE_PATHS 変数を設定します。

ENABLE_XOM=falsemake コマンドに渡すことで、実行専用バイナリをグローバルに無効にできます。

make -j ENABLE_XOM=false

検証

実行専用メモリに使用できる CTS または検証テストはありません。readelf を使用し、セグメント フラグを確認することで、バイナリを手動で確認できます。