レトポリンとは何ですか?


244

カーネルまたはクロスプロセスメモリの開示(Spectre攻撃)を緩和するために、Linuxカーネル1、いわゆるretpolineを介した間接呼び出しを実行するために-mindirect-branch=thunk-extern導入された新しいオプションコンパイルされますgcc

これは、Googleの検索でごく最近の使用のみが検出されるため(通常はすべて2018年)、新しく発明された用語のようです。

retpolineとは何ですか?それは最近のカーネル情報開示攻撃をどのように防ぎますか?


1 Linux固有ではありませんが、他のOS での緩和策の一部として、類似または同一の構造が使用されているようです。


6
Google の興味深いサポート記事
sgbj 2018年

2
/ ohtræmpəˈlin /(アメリカ)または/ ˈtræmpəˌliːn /(イギリス)と発音される
Walter

2
あなたはこれがLinuxカーネルであると言うかもしれませんが、gccそのように指しています!Linuxカーネルメーリングリストサイトと同じようにlkml.org/lkml/2018/1/3/780を認識しませんでした(一度オフラインでスナップショットが提供されたので)そこを見てもわかりません 。
PJTraill 2018年

@PJTraill-Linuxカーネルタグを追加
RichVel

@PJTraill-良い点、質問文を更新しました。Linuxカーネルで最初に見たのは、比較的オープンな開発プロセスであるためですが、オープンソースOSとクローズドソースOSのスペクトラム全体の緩和策として、同じまたは同様の手法が使用されていることに注意してください。だから私はこれをLinux固有のものとは思わないが、リンクは確かにそうだ。
BeeOnRope

回答:


158

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命令で埋めるだけの無限ループです。を使用することでLFENCEPAUSEまたはより一般的には、命令パイプラインを停止させる命令により、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予測の使用にフォールバックするため、攻撃者が推測を制御できるようになることです。


LFENCE命令は重要ではないと思います。Googleの実装では、代わりにPAUSE命令を使用しています。support.google.com/faqs/answer/7625886引用したドキュメントには、「実行されない」ではなく、「投機的に実行される」ことはありません。
ロス・リッジ

1
そのGoogle FAQページから:「上記の投機的ループの一時停止命令は正確さのために必要ではありません。しかし、それは非生産的な投機的実行がプロセッサー上でより少ない機能ユニットを占めることを意味します。」したがって、LFENCEがここで鍵となるというあなたの結論を裏付けるものではありません。
ロス・リッジ

@RossRidge私は部分的に同意しますが、これは無限ループの2つの可能な実装のように見えます。これは、一時停止/ LFENCEに続くコードを投機的に実行しないようにCPUに指示します。ただし、LFENCE 投機的実行され、投機が正しかったためにロールバックされなかった場合、メモリのロードが完了した後にのみ実行されるという主張に矛盾します。(それ以外の場合は、投機的に実行された命令セット全体をロールバックして、仕様を満たすために再度実行する必要があります)
Tobias Ribizel

1
これは、の利点があるpush/ retそれはないリターンアドレス予測スタックアンバランスを。(lfence実際の戻りアドレスが使用される前にに移動する)1つの誤予測がありますが、call+変更を使用するとrspバランスが取れretます。
Peter Cordes

1
おっと、利点オーバー push / ret(私の最後のコメントで)。re:あなたの編集:retpolineにはが含まれているため、RSBアンダーフローは不可能callです。カーネルプリエンプションがそこでコンテキストスイッチを行った場合、RSBをcallスケジューラからプライミングして実行を再開します。しかし、おそらく割り込みハンドラはret、RSBを空にするのに十分なsで終了する可能性があります。
Peter Cordes

46

retpolineは(分岐目標噴射を防止するように設計されCVE-2017から5715)を利用します。これは、カーネルの間接分岐命令を使用して、コードの任意のチャンクを投機的に実行する攻撃です。選択されたコードは、攻撃者に何らかの形で役立つ「ガジェット」です。たとえば、キャッシュへの影響を通じてカーネルデータをリークするようにコードを選択できます。retpolineは、すべての間接分岐命令をreturn命令に置き換えるだけで、この悪用を防ぎます。

retpolineの鍵となるのは、 "ret"部分だけだと思います。間接分岐をreturn命令で置き換え、CPUが悪用可能な分岐予測子の代わりに戻りスタック予測子を使用するようにします。代わりに単純なpushとreturn命令を使用した場合、投機的に実行されるコードは、関数が最終的に返すコードであり、攻撃者にとって有用なガジェットではありません。トランポリン部分の主な利点は、リターンスタックを維持することです。そのため、関数が実際に呼び出し元に戻ると、これは正しく予測されます。

ブランチターゲットインジェクションの背後にある基本的な考え方は単純です。これは、CPUがブランチターゲットバッファーにブランチのソースと宛先の完全なアドレスを記録しないという事実を利用しています。そのため、攻撃者は、特定の間接ジャンプがカーネルアドレススペースで実行されたときに予測ヒットとなる独自のアドレススペースでジャンプを使用してバッファを埋めることができます。

retpolineはカーネル情報の開示を直接防止しないことに注意してください。情報を開示するガジェットを投機的に実行するために間接分岐命令が使用されるのを防ぐだけです。攻撃者がガジェットを投機的に実行する他の手段を見つけることができる場合、retpolineは攻撃を防止しません。

論文Spectre Attacks: Paul Kocher、Daniel Genkin、Daniel Gruss、Werner Haas、Mike Hamburg、Moritz Lipp、Stefan Mangard、Thomas Prescher、Michael Schwarz、およびYuval Yaromによる投機的実行の悪用は、間接ブランチがどのように悪用される可能性があるかについて次の概要を説明します:

間接ブランチの利用。リターン指向プログラミング(ROP)を利用して、この方法では攻撃者がガジェットを選択します被害者のアドレススペースから、ガジェットを投機的に実行するよう被害者に影響を与えます。ROPとは異なり、攻撃者は被害者コードの脆弱性に依存しません。その代わり、攻撃者は分岐ターゲットバッファ(BTB)をトレーニングして、間接分岐命令からガジェットのアドレスへの分岐を誤って予測し、ガジェットの投機的な実行を引き起こします。投機的に実行された命令は破棄されますが、キャッシュへの影響は元に戻りません。これらの効果は、ガジェットが機密情報を漏洩するために使用できます。ガジェットを注意深く選択して、このメソッドを使用して、犠牲者から任意のメモリを読み取る方法を示します。

BTBを誤ってトレーニングするために、攻撃者は被害者のアドレススペースでガジェットの仮想アドレスを見つけ、このアドレスへの間接分岐を実行します。このトレーニングは攻撃者のアドレススペースから行われ、攻撃者のアドレススペースのガジェットアドレスに何が存在するかは問題ではありません。必要なのは、ブランチが同じ宛先仮想アドレスを使用するようにトレーニングするために使用されるブランチだけです。(実際、攻撃者が例外を処理している限り、攻撃者のアドレス空間のガジェットの仮想アドレスにコードがマッピングされていなくても攻撃は機能します。)また、送信元アドレスを完全に一致させる必要もありません。トレーニングに使用されるブランチのアドレスとターゲットブランチのアドレス。したがって、攻撃者はトレーニングを設定する際にかなりの柔軟性を持っています。

GoogleのProject Zeroチームによる「サイドチャネルを使用した特権メモリの読み取り」というブログエントリは、ブランチターゲットインジェクションを使用して実用的なエクスプロイトを作成する方法の別の例を示しています。


9

この質問は少し前に行われたものであり、新しい答えに値します。

エグゼクティブサマリー

「Retpoline」シーケンスは、間接分岐を投機的実行から分離できるようにするソフトウェア構造です。これは、オペレーティングシステムやハイパーバイザーの実装などの機密バイナリを、間接的なブランチに対するブランチターゲットインジェクション攻撃から保護するために適用できます。

ret poline」という言葉は、「return」と「trampoline」という言葉の組み合わせであり、「relative call」と「trampoline」から作り出された「rel poline」の改良によく似ています。これは、関連する投機的実行が無限に「跳ね返る」ことを比喩的にも保証する、戻り演算を使用して構築されたトランポリン構造です。

カーネルまたはクロスプロセスメモリの開示(Spectre攻撃)を緩和するために、Linuxカーネル[1]は、-mindirect-branch=thunk-externgccに導入された新しいオプションを使用してコンパイルされ、いわゆるretpolineを介して間接呼び出しを実行します。

[1]ただし、これはLinux固有ではありません。他のOSの緩和戦略の一部として、類似または同一の構成が使用されているようです。

このコンパイラオプションの使用は、CVE-2017-5715に必要なマイクロコードの更新が行われている影響を受けるプロセッサのSpectre V2に対してのみ保護します。(カーネルだけでなく)任意のコードで「動作」しますが、「シークレット」を含むコードのみが攻撃に値します。

これは、Googleの検索でごく最近の使用のみが検出されるため(通常はすべて2018年)、新しく発明された用語のようです。

LLVMコンパイラが持っていた-mretpolineので、スイッチを2018年1月4日の前に。その日付は、脆弱性が最初に公に報告された日です。GCC 、2018年1月7日にパッチを公開しました

CVEの日付は、この脆弱性が2017年に「発見された」ことを示唆していますが、過去20年間に製造されたプロセッサの一部に影響を与えています(したがって、ずっと前に発見された可能性があります)。

retpolineとは何ですか?それは最近のカーネル情報開示攻撃をどのように防ぎますか?

まず、いくつかの定義:

  • トランポリン -間接ジャンプベクトルと呼ばれることもあります。トランポリンは、割り込みサービスルーチン、I / Oルーチンなどを指すアドレスを保持するメモリ位置です。実行はトランポリンに飛び込み、すぐに飛び出したり、跳ねたりするため、トランポリンと呼ばれます。GCCは、ネストされた関数のアドレスが取得されると、実行時に実行可能なトランポリンを作成することにより、伝統的にネストされた関数をサポートしてきました。これは、通常はスタックの、含まれている関数のスタックフレームにある小さなコードです。トランポリンは静的チェーンレジスタをロードしてから、ネストされた関数の実際のアドレスにジャンプします。

  • サンク -サンクは、別のサブルーチンに追加の計算を注入するために使用されるサブルーチンです。サンクは主に、結果が必要になるまで計算を遅らせるため、または他のサブルーチンの最初または最後に操作を挿入するために使用されます

  • メモ化 -メモ化関数は、結果は、特定の入力の一部のセットに対応する「記憶します」。入力を記憶した後続の呼び出しは、再計算するのではなく、記憶した結果を返すため、これらのパラメーターを使用して関数に対して行われた最初の呼び出し以外のすべてから、特定のパラメーターを使用した呼び出しの主要なコストを排除できます。

非常に大まかに、retpolineがあるトランポリンリターンとしてサンク「に、台無しにメモ化、間接分岐予測器インチ

ソース:retpolineにはIntel用のPAUSE命令が含まれていますが、AMDではLFENCE命令が必要です。これは、そのプロセッサではPAUSE命令がシリアル化命令ではないため、戻りを待機していると推測されるため、pause / jmpループが過剰な電力を使用するためです。正しいターゲットに予測を誤ります。

Arstechnicaは問題の簡単な説明を持っています:

「各プロセッサーには、アーキテクチャ上の動作(命令がどのように機能するかを記述し、プログラマーがプログラムを作成するために依存する文書化された動作)とマイクロアーキテクチャ上の動作(アーキテクチャの実際の実装の動作方法)があります。これらは微妙に異なる場合があります。たとえば、アーキテクチャ的には、メモリ内の特定のアドレスから値をロードするプログラムは、アドレスが判明するまで待機してから、ロードを実行しようとします。ただし、マイクロアーキテクチャ的には、プロセッサがアドレスを推測して推測できるようになるため、メモリから値をロードする(遅い)が、使用するアドレスが確実に決まる前であっても。

プロセッサが間違って推測した場合、推測されたatの値を無視して、今度は正しいアドレスでロードを再度実行します。したがって、アーキテクチャで定義された動作が保持されます。しかし、その誤った推測は、プロセッサの他の部分、特にキャッシュの内容を妨害します。これらのマイクロアーキテクチャーの障害は、悪意のあるプログラムがメモリに保存されている値について推論できるように、キャッシュにある(またはすべきでない)データにアクセスするのにかかる時間を計ることによって検出および測定できます。

Intelの紙から: " Retpoline:A分岐ターゲットインジェクション軽減 "(.PDF):

「retpolineシーケンスは、プロセッサの投機的実行が「間接分岐予測子」(プログラムフローを予測する1つの方法)を使用して、エクスプロイトによって制御されたアドレスに投機することを防ぎます(分岐ターゲットインジェクションの5つの要素の要素4を満たす(Spectreバリアント2 )上記の構成を悪用します。」

要素4は次のとおりです。「エクスプロイトは、この間接分岐に影響を与えて、ガジェットを推測的に誤実行し、実行する必要があります。このガジェットは、エクスプロイトによって選択され、通常はキャッシュタイミングによって、サイドチャネルを介して秘密データをリークします。」

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.