ShadowCallStack(SCS)は、(スタック バッファ オーバーフローに似た)戻りアドレスの上書きを防ぐ LLVM インストルメンテーション モードです。戻りアドレスの上書きを防ぐために、非リーフ関数の関数プロローグで別の場所に確保した ShadowCallStack に関数の戻りアドレスを保存し、関数エピローグで ShadowCallStack から戻りアドレスを読み込みます。戻りアドレスは、スタックの巻き戻し(アンワインド)に備えて通常のスタックにも格納されますが、この目的以外では使用されません。これにより、通常のスタック上の戻りアドレスを変更する攻撃がプログラム制御フローに影響しないことが保証されます。
aarch64 では、インストルメンテーションは x18
レジスタを利用して ShadowCallStack を参照します。つまり、ShadowCallStack への参照をメモリ内に保存する必要がなく、そのため、任意のメモリを読み取れる攻撃者によって ShadowCallStack のアドレスが開示されないランタイムを実装できます。
実装
Android は、カーネルとユーザー空間の両方で ShadowCallStack をサポートしています。
カーネルで SCS を有効にする
カーネルで ShadowCallStack を有効にするには、カーネル構成ファイルに次の行を追加します。
CONFIG_SHADOW_CALL_STACK=y
ユーザー空間で SCS を有効にする
ユーザー空間コンポーネントで ShadowCallStack を有効にするには、コンポーネントのブループリント ファイルに以下の行を追加します。
sanitize: { scs: true }
SCS は、x18
レジスタが ShadowCallStack のアドレスを格納するために予約されており、他の目的に使用されないことを前提にしています。すべてのシステム ライブラリがコンパイルされて x18
レジスタが予約されますが、プロセス内の以前のコードと相互実行されるユーザー空間コンポーネント(たとえば、サードパーティ製アプリによって読み込まれる可能性があるライブラリ)で SCS が有効になっている場合、x18
レジスタを上書きする可能性があるため、潜在的に問題となりえます。そのため、SCS は以前のバイナリに読み込まれない自己完結型コンポーネントでのみ有効にすることをおすすめします。
検証
SCS 専用の CTS テストはありません。SCS がデバイスに影響しないことを確認するには、SCS を有効または無効にして CTS テストをパスするかどうかを検証してください。