GoogleのPaul Turnerが書いたコメントでsgbjが言及した記事は、次のことをより詳細に説明していますが、私が試してみましょう。
現時点で限られた情報からこれをつなげることができる限り、retpolineは、CPUが間接ジャンプのターゲットを推測することを防ぐために実行されない無限ループを使用するリターントランポリンです。
この問題に対処する基本的なアプローチは、Andi Kleenのカーネルブランチで確認できます。
スタックの一番上に__x86.indirect_thunk
メモリアドレス(これを呼び出すADDR
)が格納されている呼び出しターゲットを読み込み、RET
命令を使用してジャンプを実行する新しい呼び出しを紹介します。サンク自体は、NOSPEC_JMP / CALLマクロを使用して呼び出されます。このマクロは、多くの(すべてではないにしても)間接呼び出しとジャンプを置き換えるために使用されていました。マクロは単に呼び出しターゲットをスタックに配置し、必要に応じて戻りアドレスを正しく設定します(非線形制御フローに注意してください)。
.macro NOSPEC_CALL target
jmp 1221f /* jumps to the end of the macro */
1222:
push \target /* pushes ADDR to the stack */
jmp __x86.indirect_thunk /* executes the indirect jump */
1221:
call 1222b /* pushes the return address to the stack */
.endm
call
間接呼び出しが終了したときに制御フローがNOSPEC_CALL
マクロの使用の背後で継続するように、最後にを配置する必要があるため、通常のマクロの代わりに使用できます。call
サンク自体は次のようになります。
call retpoline_call_target
2:
lfence /* stop speculation */
jmp 2b
retpoline_call_target:
lea 8(%rsp), %rsp
ret
制御フローはここで少し混乱する可能性があるので、はっきりさせておきます。
call
現在の命令ポインタ(ラベル2)をスタックにプッシュします。
lea
スタックポインターに 8を追加し、最後にプッシュされたクワッドワード(ラベル2への最後の戻りアドレス)を効果的に破棄します。この後、スタックの先頭は再び実際の戻りアドレスADDRを指します。
ret
*ADDR
スタックポインターにジャンプして、呼び出しスタックの先頭にリセットします。
結局のところ、この動作全体は、に直接ジャンプするのと実質的に同じ*ADDR
です。得られる1つの利点は、returnステートメント(Return Stack Buffer、RSB)に使用される分岐予測子が、call
命令を実行するときに、対応するret
ステートメントがラベル2にジャンプすると想定していることです。
ラベル2の後の部分は実際には決して実行されず、理論上は命令パイプラインをJMP
命令で埋めるだけの無限ループです。を使用することでLFENCE
、PAUSE
またはより一般的には、命令パイプラインを停止させる命令により、CPUがこの投機的実行に電力と時間を浪費するのを防ぎます。これは、retpoline_call_targetの呼び出しが正常に戻るLFENCE
場合、次の命令が実行されるためです。これは、分岐予測器が元の戻りアドレス(ラベル2)に基づいて予測するものでもあります。
Intelのアーキテクチャマニュアルから引用するには:
LFENCEに続く命令は、LFENCEの前にメモリからフェッチできますが、LFENCEが完了するまで実行されません。
ただし、仕様では、LFENCEとPAUSEによってパイプラインが停止することについては決して言及されていないため、ここでは行間のビットを少し読み取っています。
ここで、元の質問に戻ります。カーネルメモリ情報の開示は、2つのアイデアの組み合わせにより可能です。
投機的実行は、投機が間違っていた場合は副作用がないはずですが、投機的実行は依然としてキャッシュ階層に影響します。つまり、メモリロードが投機的に実行された場合でも、キャッシュラインが削除される可能性があります。キャッシュ階層のこの変更は、同じキャッシュセットにマップされているメモリへのアクセス時間を注意深く測定することで識別できます。
読み取ったメモリのソースアドレス自体がカーネルメモリから読み取られた場合、任意のメモリの一部のビットをリークすることもできます。
Intel CPUの間接分岐予測子は、ソース命令の最下位12ビットのみを使用するため、ユーザーが制御するメモリアドレスを使用して、2 ^ 12のすべての予測履歴を簡単に汚染できます。これらは、カーネル内で間接ジャンプが予測されると、カーネル特権で投機的に実行できます。したがって、キャッシュタイミングサイドチャネルを使用すると、任意のカーネルメモリをリークできます。
更新:カーネルメーリングリストでは、リターンスタックバッファー(RSB)が空で実行されるとき、より最近のIntelアーキテクチャ(Skylake +)がフォールバックするため、retpolinesは分岐予測の問題を完全に緩和しないと私を導く進行中の議論があります脆弱なブランチターゲットバッファ(BTB):
緩和戦略としてのRetpolineは、間接分岐をリターンに交換し、攻撃者が毒する可能性があるため、BTBからの予測を使用しないようにします。Skylake +の問題は、RSBアンダーフローがBTB予測の使用にフォールバックするため、攻撃者が推測を制御できるようになることです。