Meltdown

Negative Result: Reading Kernel Memory From User Mode
https://cyber.wtf/2017/07/28/negative-result-reading-kernel-memory-from-user-mode/

1. メモリサブシステム

  • CPUが(仮想アドレスに対して)load命令を発行すると L1cacheからデータを読み出す
  • L1cacheはVIPT(Virtually Indexed, Physically Tagged)である。
  • L1cacheにデータが無ければ L2cacheに取りにいく
  • このときページテーブルで仮想アドレスを物理アドレスに変換する。
  • L2cache, L3cacheは PITP (Physically Indexed, Physically Tagged)である。

2. 投機実行

  • パイプラインは命令デコードで始まる
  • デコードによりマイクロ命令に変換される
  • マイクロ命令はリオーダバッファにキューされる
  • キューされたマイクロ命令は必要な依存が解決されたものから実行ユニットで実行される
  • 実行結果はリオーダバッファに格納され、別のマイクロ命令の依存解決に使われる
  • ある命令に対応するマイクロ命令が全て実行され、リオーダバッファの先頭に移動したあとリタイア処理に入る。
  • 例外が発生したときは、マイクロ命令が属する命令を割り込みでリタイアする。


3. 投機実行の悪用

以下の命令がユーザモードで実行されたものとする。


mov rax, [somekernelmodeaddress]


これは割り込みを起こす。しかし命令実行が終わってから実際にリタイアするまでに何が起きるか明確でない。


mov rax, [somekernelmodeaddress]
mov rbx, [someusermodeaddress]


依存がなければこの2命令は同時に実行される。(ロードの実行ユニットが2ユニットあるものとする)
最初のmov命令が割り込みを起こすので、2番目のmovではレジスタを更新しない。しかし2番目のmovは投機実行されており、マイクロ命令がcacheにsomeusermodeaddressをロードしているかもしれない。


mov rax, [somekerneladdress]
and rax, 1
mov rbx, [rax + someusermodeaddress]


後者の2命令が投機実行されたのなら、ロードされたアドレスは、somekerneladderss からロードした値に依存して異なっている。そしてキャッシュにロードしたアドレスはロードすべきキャッシュラインと異なる。

最初の問題は2番目、3番目の命令が1番目の命令がリタイアする前に実行されることである。競合がある。リオーダバッファを依存があり異なる実行ユニットを使う命令で埋める。その後上の命令を実行する。依存があるのでCPUは1つのマイクロ命令しか実行できない。

これはいくつかの仮定をしている。

1. 割り込みフラグに関わらず投機実行される
2. 投機実行とリタイアの競合に勝つ
3. 投機実行の間にキャッシュをロードできる
4. 割り込みフラグに関わらずデータが提供される