1. ホーム

[解決済み】レトポリンとはどのようなもので、どのように機能するのですか?

2022-04-05 06:46:18

質問

カーネルまたはプロセス間のメモリ漏洩を軽減するために、( スペクター 攻撃)に対抗することができます。 Linuxカーネル 1 は、新しいオプションでコンパイルされます。 , -mindirect-branch=thunk-extern に導入されました。 gcc を介して間接的な呼び出しを行うために、いわゆる レトポリン .

Googleで検索するとごく最近の使用例しか出てこないので、新しく考案された用語のようです(概ね全て2018年)。

レトポリンとは何か、また最近のカーネル情報漏洩攻撃をどのように防ぐのか?


1 しかし、これはLinuxに限ったことではなく、似たような、あるいは同一の構成が 緩和戦略 他のOSでは

解決方法は?

記事 sgbjさんがコメントで紹介されている、GoogleのPaul Turner氏が書いた以下の内容は、もっと詳しく説明されているのですが、一応ご紹介しておきます。

現時点での限られた情報から整理する限り、レトポリンとは リターントランポリン というのは、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の呼び出しが正常に返される場合、retpoline_call_targetの呼び出しに含まれる LFENCE が次に実行される命令となります。これは、分岐予測器が元の戻りアドレス(ラベル2)から予測するものでもあります。

インテルのアーキテクチャマニュアルから引用します。

<ブロッククオート

LFENCE に続く命令は、LFENCE の前にメモリからフェッチすることができますが、LFENCE が完了するまでは実行されません。

ただし、LFENCEやPAUSEがパイプラインをストールさせるということは仕様には書かれていないので、ここはちょっと行間を読むことになりますね。

さて、最初の質問に戻ります。 カーネルメモリの情報漏洩は、2つの考え方の組み合わせで可能になります。

  • 投機実行は副作用がないはずなのに、その投機が間違っていたとき。 投機的実行はまだキャッシュ階層に影響を与える . つまり、メモリロードが投機的に実行された場合、やはりキャッシュラインを退避させる原因となっている可能性があるのです。このキャッシュ階層の変化は、同じキャッシュセットにマッピングされたメモリへのアクセスタイムを注意深く測定することで確認することができます。

    メモリ読み出しのソースアドレス自体がカーネルメモリから読み出されたものである場合、任意のメモリの何ビットかをリークすることも可能です。

  • Intel CPUの間接分岐予測器は、ソース命令の下位12ビットしか使用しないため、ユーザーが管理するメモリアドレスで2^12通りの予測履歴をすべて毒することは容易である。これにより、カーネル内で間接ジャンプが予測された場合、カーネル権限で投機的に実行することができます。キャッシュタイミングサイドチャネルを使用すると、任意のカーネルメモリをリークすることができます。

UPDATE: カーネルメーリングリスト というのも、Return Stack Buffer (RSB) が空になると、最近の Intel アーキテクチャ (Skylake+) は脆弱な Branch Target Buffer (BTB) にフォールバックしてしまうからです。

<ブロッククオート

緩和策としてのRetpolineは、間接ブランチをリターンに置き換えます。 BTBから来る予測は使われないようにするためです。 攻撃者によって毒殺されます。 Skylake+ での問題は、RSB のアンダーフローが発生した場合、RSB を使用するようにフォールバックすることです。 BTB予測は、攻撃者が投機の制御を行うことを可能にします。