SlideShare une entreprise Scribd logo
1  sur  107
Télécharger pour lire hors ligne
本当にわかる
Spectre と Meltdown
川田裕貴 @hktechno
1
セキュリティ・キャンプ全国大会2018
講義資料 URL: https://goo.gl/uTwX2c
自己紹介
@hktechno 川田 裕貴 (かわた ひろたか)
● LINE 株式会社 開発1センター LINE 開発1室
○ メッセンジャープラットフォームの開発
○ 主に、スタンプ・絵文字・着せかえ、たまに IoT
● 自作 CPU 開発にハマっていた
○ アウト・オブ・オーダー実行できる自作 CPU
○ 主にコンパイラを作っていた
○ http://open-arch.org/
○ http://amzn.asia/gtThKbh
2
経歴?
2008年 セキュリティ・プログラミングキャンプ参加
2009, 2010, 2012 チューター, 2015 講師
2009年 筑波大学 情報学群 情報科学類 入学
2011年 IPA 未踏IT人材発掘・育成事業採択
(スーパークリエイターという謎の称号を頂く)
2014年 筑波大学大学院 コンピュータ・サイエンス専攻 入学
2016年 LINE 株式会社 入社
3
本日の進め方
● CPU の基本的な仕組み: 60分
○ パイプライン、キャッシュ、投機的実行、分岐予測
○ Spectre / Meltdown の理解に必要な知識の説明
● Spectre / Meltdown の説明: 30分
● Spectre Variant 1 PoC を作ってみよう: 90分
○ Spectre Variant 1 の対策を考えてみよう
○ Spectre Variant 1 の考えた対策を実装してみよう
● Meltdown の PoC デモとか: 10分
● Spectre Variant 4 の説明: 10分
○ Foreshadow の説明も...?
4
Spectre・Meltdown って何?
5
Spectre と Meltdown について
CPU を対象としたサイドチャネル攻撃に対する脆弱性
● 2018年1月に Google の Project Zero などが共同で公開した
論文が発端
○ https://meltdownattack.com/
● オリジナルの手法は大きく分けて3つ (Variant 1,2,3)
○ Variant 3 のことを特に Meltdown と呼ぶ
● 現代の CPU アーキテクチャを巧妙に操ることにより、
サイドチャネル攻撃を可能に
○ 具体的には、メモリ内容の吸い取り
6
Spectre / Meltdown の手法の概要
公開当初、大きく3つの手法が公表された
https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html
Code Name 通称 CVE
Spectre
Variant 1 Bounds Check Bypass CVE-2017-5753
Variant 2 Branch Target Injection CVE-2017-5715
Meltdown
Variant 3 Rouge Cache Data Load CVE-2017-5754
7
Spectre / Meltdown その後
その後、同じような脆弱性がいくつも発見されている...
Code Name 通称 CVE
Meltdown Variant 3a Rouge System Register Read CVE-2018-3640
SpectreNG Variant 4 Speculative Store Bypass CVE-2018-3639
Foreshadow (-NG) L1 Terminal Fault
CVE-2018-3615
CVE-2018-3620
CVE-2018-3646
8
Spectre / Meltdown の手法の概要
条件分岐予測の悪用
分岐予測をトレーニングし、
メモリアクセスの境界値
チェックをバイパスして実行
例えば...
本来は読み取りが行われ
ないはずのメモリ領域の
データを、吸い出すことが
可能。
Spectre Variant 2
間接分岐予測の悪用
間接分岐予測テーブルの
挙動を解析し、分岐予測を
同じキーになるアドレスでト
レーニングした上で狙った
コードを実行
例えば...
本来は読み取りが行われ
ないはずのメモリ領域の
データを、吸い出すことが
可能。
Meltdown (Variant 3)
Out of order 実行の悪用
特権チェックバイパス
本来は例外が発生し実行さ
れないコードを、特権レベ
ルのチェックをバイパスして
実行
例えば...
本来は読み取ることができ
ないメモリ領域のデータを、
吸い出すことが可能。
Spectre Variant 1
9
サイドチャネル攻撃とは
攻撃対象に直接手を加えるのではなく、動作を観察することによ
り、その挙動の違いから情報を得ようとする手法
● Spectre, Meltdown においては、本来読み取れない領域にあ
るメモリ内のデータを推測する手法
○ 具体的には、メモリアクセスの時間の差を使う
● メモリ内のデータを ”直接” 読み取るわけではない
○ (実際は読み取っているわけだが... 詳細は後述)
● 元のプログラムに手を加える必要がなく、
攻撃を成功することができる (可能性がある)
10
Meltdown と Spectre の難しい点
よく CPU アーキテクチャを理解していないと、理解困難
● アセンブリが書けるだけでは、知識が足りない
● 繊細な CPU の気持ちを、よくわかっている必要がある
ハードウェア由来の脆弱性、ソフトウェアでの対策が困難
● 不可能ではないが、パフォーマンスの低下を伴う
● Spectre Variant 2 については、一部のアーキテクチャでは
ハードウェア支援がないと対策がとても難しい
11
サイドチャネル攻撃 - Flush + Reload
メモリアクセスのレイテンシの差を利用した攻撃
1. キャッシュをクリア (特に、以下の array2 の領域)
○ キャッシュを追い出す (Eviction) させてもいい
2. メモリを間接的にアクセスさせる
○ y = array2[array[x] * 4096]
○ array[x] のメモリ内容を基に array2 にアクセスする
○ 4096 をかけるのは、プリフェッチを防ぐため
3. array2 のアクセスレイテンシを調べる
○ キャッシュに乗っていればレイテンシ低⇓
○ 過去にアクセスされた可能性大 => 値がわかる
12
時間のかかる何か
if (Array 境界値チェック)
array1[array2[x]]
その先の何か
サイドチャネル攻撃 - Flush + Reload
array2
攻撃対象
array1
CPU
時間かかるなぁ
ここはよく true だから今回も true のときの
処理を投機的に実行だ!
間違ってたらあとでやりなおす!
13
サイドチャネル攻撃 - Flush + Reload
array2
攻撃対象
data[x] = 0xa
array1
CPU
時間のかかる何か
if (Array 境界値チェック)
array1[array2[x]]
その先の何か
まだ終わってない
x は確定していた!
array[0xa] もアクセスできる!
キャッシュに乗る!
準備完了
先に実行だ!
14
サイドチャネル攻撃 - Flush + Reload
array2
攻撃対象
data[x] = 0xa
array1
CPU
array1 を Flush
時間のかかる何か
if (Array 境界値チェック)
array1[array2[x]]
その先の何か
array1 を Reload
array1 のキャッシュを消す
array1 のアクセスレイテンシをチェック
過去にアクセスしたことがあれば速い。
レイテンシの差で、array2 のデータがわかる...
15
基本的な CPU の仕組み
命令セットだけではわからない、CPU 内部の深い仕組み
16
低レイヤとは
システムソフトウェア
VM
OS kernel
アセンブラ
バイナリ
CPUアーキテクチャ
CPUのお話
その他のなにか
17
低レイヤとは
システムソフトウェア
VM
OS kernel
アセンブラ
バイナリ
CPUアーキテクチャ
その他のなにか
今日の話
18
外の世界から見える CPU
CPU の違いってなんだろう?
● 命令セットの違い
○ ARM vs x86
○ 命令が増えた (e.g. AVX-512 とか...)
● クロック (2.4GHz, 3GHz, 4GHz…)
○ クロック数が変わってないから速度が変わらない?
● プロセス (14nm, 10nm, Tri-gate transistor…)
○ プロセスが細かくなったら何が良いの?
CPU って中身どうなってるの???
19
基本的な CPU パイプライン (MIST32 - In order)
● 大学の授業で出てきそうな構成?
○ Instruction Fetch
○ Instruction Buffer
○ Instruction Decode
○ Dispatch
○ Execution
IB
20
Intel Skylake の内部
これが現実 ⇨
● 現代の CPU はとても複雑
○ これでも単純化済
● 命令の流れは一緒?
○ いろんなバッファ
○ 並列化
● 日々進化する内部構造
/(^o^)\ よくわからない
わかると楽しい\(^o^)/
21
Skylake の内部のパイプライン
Skylake の詳細ブロック図
● Frontend
○ フェッチ
○ デコーダ (x86 では重要)
● Execution Engine
○ 実行ユニット
○ 投機的実行の核心部
● Memory Subsystem
○ データ用メモリアクセス部
22
Intel x86 の内部的な命令: uOps (Micro-Ops)
よく見る x86 の命令は、内部的な実行命令とは違う
● x86 アセンブラ1命令 => 複数の uOps
○ 最近は、逆の場合もある (Macro Fusion)
● なぜ命令を変換するのか?
○ x86 は CISC (命令がいろいろできるが複雑)
○ 時代・効率・回路規模は RISC
○ 設計が古いが、“互換性が重要”
○ 命令セットの設計に依存しない実行回路に
● Pentium III (P6) 以降に採用
23
Intel x86 のフロントエンド
デコーダが複雑
だが強い RISC に負けないように...
● 命令セットがクソ 設計が大変古い
=> uOps で解決
○ デコーダが大変複雑
○ 消費電力、回路規模に悪影響...
● デコーダをなるべく動かさない設計
○ 1度デコードしたところは、再度デコードしない
○ uOp Cache (以前は Trace cache)
○ Loop stream detector
24
Intel x86 における、マイクロコード (Microcode)
ある1命令を、プログラム可能な uOps に変換する仕組み
● 利用目的
○ ハードウェアの不具合 (エラッタ) の修正
○ 複雑な命令の
● ハードウェアは基本的に修正パッチを当てられない
○ 出荷した後は不具合が修正不可能
○ マイクロコードなら後からパッチを当てられる
● 利用例
○ Haswell における TSX 無効化
○ Spectre / Meltdown 対策における命令追加
25
Intel x86 の実行ユニット
1コアの中に、複数の並列実行可能な実行ポート
● スーパースカラ + 投機的実行
○ 投機的実行の詳細は後ほど詳しく
● Skylake では、実行ポートは8ポート
○ Haswell 以降増えた
https://www.anandtech.com/show/6355/intels-haswell-architecture/8
● ポートごとにできることが違う
○ ALU, FPU
○ Branch
○ Load / Store
26
Intel x86 のキャッシュとメモリアクセス
● L1, L2, LLC (L3) の 3-Layer キャッシュ
○ L1 は Instruction, Data の2つのキャッシュ
● キャッシュラインは 64 byte
○ L1, L2: 8-way セットアソシアティブキャッシュ
○ 基本的にこの単位で、領域がキャッシュされる
● ページサイズは、基本的に 4096 byte
○ ページ境界を超えてプリフェッチはされない
27
現代の CPU と
Meltdown / Spectre の関係
28
Meltdown / Spectre がなぜ起きるのか
現代の CPU が命令を実行する際に行う、高度なハードウェアレ
ベルでの最適化が原因
● 特に、”投機的実行 (Speculative Execution)” と呼ばれる、命
令を先読みして高速化する手法が問題
CPU 内で行われる投機的実行の例
● 命令のプリフェッチ
● ☆ アウト・オブ・オーダー実行 (順序を守らない実行)
● ☆ 分岐予測器を組み合わせた投機的実行
29
“投機的” に命令列を実行して、間違っていたら結果を捨てる
● 書かれた命令の順番通りには実行しないこともある
○ 何もしないで待っているより、何かを実行
○ とにかく、パイプラインに隙間が生まれないように
● 間違って、実際には実行しない命令を、先読みして実行してし
まうことがある
○ 最終的な結果には影響が出ないように結果を捨てる
○ 現代の CPU では、間違うことはかなり少ない
うまく使うと脆弱性につながる可能性が...?
現代の CPU における投機的実行
30
Out of Order
Execution
アウト・オブ・オーダー実行
CPU の命令が上から順番に実行され
るなんて、誰が言った!
31
アウト・オブ・オーダー実行
(Out of Order Execution, OoO)
先読みした命令の依存関係を解析して、実行できる命令から実際
の記述順序を無視して先に実行する
● 命令間のレジスタ依存関係を動的に解析する
○ 例えば、即値ロード命令は他の命令と依存関係がない
○ 即値ロード命令の例: mov eax, 0x1
● CPU は、複数の命令実行ポートを持っている
○ すべての命令が同時に実行完了するとは限らない
● 依存しているレジスタの結果が準備されたら、先に実行
○ レジスタマッピングや、書き戻しのバッファリングにより整
合性を保つ
32
アウト・オブ・オーダー実行の例
mov eax, [eax]
xor ebx, ebx
add ebx, eax
inc ecx
add eax, ecx
Load (遅い)
↑の命令とは依存がない
↑の命令とは依存がない
※ 簡単な例で、特にアセンブリに意味はない
33
アウト・オブ・オーダー実行の例
mov eax, [eax]
xor ebx, ebx
add ebx, eax
inc ecx
add eax, ecx
1
1
2
1
2
⇓実行順
命令の順番を入れ替えても構わない
しかも、開いてる実行ポートで並列に実行できる
Load (遅い)
↑の命令とは依存がない
先に実行が可能
↑の命令とは依存がない
先に実行が可能
34
OoO に関する疑問・破壊する常識
● あるレジスタ (eax や ebx) は CPU 内に1つじゃないの?
○ レジスタ・リネーミングを利用しています
○ 実はすべてただのエイリアスです
● CPU 内部で ”同時” に実行できる命令は1つじゃないの?
○ 複数の命令実行ポートが並列に並べられています
○ 例えば、Haswell 以降なら8実行ポート
● メモリアクセスを行う命令も並列に実行するの?
○ Intel の最近の CPU はメモリアクセスも OoO です
● なんでコンパイラでやらないの?
○ 成功していたら Itanium は死んでいない (VLIW)
35
アウト・オブ・オーダー実行を支える技術 - 1
レジスタ・リネーミング (Register Renaming)
● 物理レジスタを、内部の仮想レジスタにリネームする
● 命令の依存をより少なくできる
mov eax, [eax]
inc eax
mov [eax], eax
mov eax, ebx
mov eax, [eax]
同じ eax レジスタ
しかし、依存はない
実は先に実行可能
36
アウト・オブ・オーダー実行を支える技術 - 1
レジスタ・リネーミング (Register Renaming)
● 内部的なレジスタファイル内の物理レジスタと、論理レジスタ
(eax, ebx…) との対応を持つ
○ 論理レジスタの数倍の物理レジスタを持っている
mov r1, [r1]
inc r1
mov [r1], r1
mov r3, r2
mov r3, [r3]
レジスタ依存はない
実は先に実行可能
37
レジスタ・リネーミング (Register Renaming)
まとめると...
● Intel x86 のレジスタ少なすぎない?
○ どうせリネーミングするからあまり問題にならない
● 内部的なレジスタファイルの大きさ (Skylake)
○ Integer 180個
○ Vector 168個
● リネーミング時に依存性の排除を行う
○ 0 や 1 の即値やクリアは特殊な扱いをする場合も
○ Zeroing Idioms, Ones Idioms
38
アウト・オブ・オーダー実行を支える技術 - 2
Tomasulo のアルゴリズム
● Reservation Station
○ 命令の依存関係を待つところ
○ ソースレジスタが利用可能かを監視して、 実行可能であれ
ば実行を開始する
○ Unified な方式、実行ポートごとに持たせる方式
● Re-order Buffer
○ 命令のコミットを制御するバッファ
○ 演算の結果は一度ここへ書かれる
※ 実際の Intel の CPU がこの通り実装されているわけではない
39
Reservation Station の例
Reservation Station
命令1 SRC1
NG
SRC2
OK
命令2 NG NG
命令3 OK NG
命令4 OK OK
Reorder Buffer,
Register File
Execution Unit へ 40
Reorder Buffer を使ったデータの流れ
Reorder Buffer Stage
1. 割り当て
Writeback Stage
2. 結果の書き込み
Retire / Commit
Reorder Buffer
Register File
3. コミット
※ 実際の Intel の CPU がこの通り実装されているわけではない 41
メモリアクセスのアウト・オブ・オーダー実行
レジスタとだいたい同じような仕組みで実装可能
● メモリアクセスも順序は入れ替わる
○ LOAD 命令はできるものから先に実行
○ 同じアドレスの LOAD は STORE より先に実行しない
○ Store Data の実行ポートは1つしかない
● 専用のバッファを持つ
○ 多分、専用のスケジューラを持っている
○ バッファのサイズ以上は先読みされない
42
Reservation Station & Reorder Buffer
まとめると...
● CPU 内部では Reorder Buffer と Reservation Station の仕
組みにより、命令の実行順序が入れ替わる
● メモリアクセスも順番が入れ替わることがある
Skylake における実装
● Reorder Buffer 224 エントリ
● Reservation Station 97 エントリ
43
Branch
Prediction
分岐予測
運命の分かれ道
44
分岐予測 (Branch Prediction)
ブランチ命令で分岐する場合、行き先を予測して先に命令を読み
出し・実行を行う
● Intel x86 でいうとジャンプ系の命令 Jxx (jne, je, jz…)
● 分岐するかしないか (“Taken” or “Not Taken”) および、
その分岐先をハードウェアで動的に予測する
○ 基本的に、過去の分岐結果をもとに分岐先を予測する
○ CPU 内部に過去の分岐履歴を保存している
● 予測した分岐先の命令を先読みして、実行する
○ 現代の CPU では予測成功率 99% 以上
(個人的な感想)
45
分岐予測の例 - 簡単な分岐命令
mov eax, [eax]
cmp eax, 0
je is_zero
...
...
is_zero:
...
...
実行完了するまで
Jump するかわからない
Load (遅い)
Block 1
Block 2
Block1 or Block2
どちらかを投機的に実行したいが、高
確率で正解を選びたい
基本的な if 文であれば PC 相対ジャンプであることが多い
46
分岐予測器ってどこにいるの
フロントエンドにいる
● 分岐先アドレスを予測
● ブランチ:
CPU にとって大きなコスト
● 分岐ミス:
長いパイプラインが止まる
❌ 分岐予測成功したらラッキー
⭕ 分岐予測ミスは致命的
47
分岐予測器 (Branch Predictor)
古典的な実装
● 分岐しない (or する) 方向で常に予測しておく
○ x86 だと Pentium 以前の CPU はこれ (i486 とか)
● 飽和カウンター (Bi-modal Counter)
○ 4つのステートを持って、1度ミスしただけでは予測方向が
変わらないような方式
○ ループの分岐予測には有効
48
分岐予測器 - 最近の CPU の実装
いくつかの方式の組み合わせで、あらゆるパターンでも予測
● 局所的分岐予測
○ 同じアドレスの命令の、過去の分岐履歴を利用
○ 同じアドレスで、同じパターンを繰り返す場合有用
● グローバル分岐予測
○ 過去のコア全体の分岐履歴のパターンをもとに、次の分岐
の挙動を予測
● 上記の組み合わせを bit 演算で行い、予測テーブルを引く
○ PC + 局所履歴 + グローバル履歴
49
分岐予測の例 - 絶対アドレス間接ジャンプ
(Indirect Branch)
レジスタに格納されたアドレスにジャンプする場合
● 例えばjXX [eax], call [eax]
● レジスタに格納されたアドレスが確定するまで、投機的実行が
できない
○ これまでの手法は、PC 相対 + 即値ジャンプの場合
○ 間接ジャンプで、特にLoad が噛むととても遅い
● 間接ジャンプ先も、実は予測可能では?
○ 同じ PC のジャンプは、よく同じアドレスにジャンプするので
はないか?
※ PC (Program Counter): Intel では IP (Instruction Pointer)
実行中命令のアドレスのこと
50
間接分岐予測の概要
Intel x86 の最近の実装
Indirect Branch はちょっと特殊
アドレスだけでなく、history も
51
Branch Target Buffer (BTB)
ブランチ命令のジャンプ先アドレスを記憶しておくバッファ
● 同じ PC のジャンプは、よく同じ場所に飛ぶことが多い
○ 間接的 (Indirect branch) であっても同じ
○ これをキャッシュしておけば、投機的実行を実際のアドレス
確定前に行うことが可能
● PC => ブランチ先 PC のバッファを持つ
○ 最近の Intel x86 CPU の場合...
○ PC を XOR で圧縮して、BTB のキーとする
○ キー内の一部の bit は他の bit と共有される
○ つまり、エントリを共有する場合がある
52
BTB の構造 - Intel x86 Direct Branch Prediction
下位 31bit + XOR Folding でビット演算をして Lookup
● 下位 13 bit はそのまま
● 残りの 18bit に対して謎の XOR Folding を行う
○ 下記表参照 ⇨
○ 規則性...?
● エントリの内容
○ 下位32bitのアドレス
○ 4GB (2^32) を超えてジャンプ
するかどうか
○ 上位 32bit のアドレス
bit A bit B
0x40.0000 0x2000
0x80.0000 0x4000
0x100.0000 0x8000
0x200.0000 0x1.0000
0x400.0000 0x2.0000
0x800.0000 0x4.0000
0x2000.0000 0x10.0000
0x4000.0000 0x20.0000
53
余談: なぜ BTB は頑張ってビット幅を減らすのか
連想メモリ (CAM) は回路規模が厳しいから (多分)
● 大きな bit 列を比較 == 大きな規模のコンパレーター
○ エントリ数 * bit 幅に比例して大きくなる
○ 半分のビット幅にできたら、エントリが沢山入る
○ ただ、比較をするための回路でも馬鹿にできない
● ハードウェア上に大きな連想メモリを置くのは難しい
○ キャッシュ, TLB, BTB…
○ いろいろなものが、CAM の制限を受けている
○ 回路を作ってみるとわかるかも?
54
BTB の概念と間接ジャンプの場合
分岐予測器はフロントエンドにいる
● パイプラインの隙間を開けない
○ ブランチを検知したら次のアドレスをすぐに確定
○ 予測したブランチ先アドレスから、命令をフェッチ
○ 命令をデコードしたり、計算している暇はない
● レジスタを使った間接ジャンプでない場合は...
○ true or false で分岐先が確定
○ 基本的に、分岐先を格納するものは1つで良い
間接ジャンプの場合は...? 分岐先の候補が複数あるかも?
55
BTB の構造 - Intel x86 - Indirect Branch
Prediction
複数ある分岐予測先を予測するために...
● Branch History Buffer (BHB) を使う
○ 分岐元下位ビットを Branch buffer state に利用
○ Branch buffer state は、右シフトしつつ新しい情報が加え
られる
● 過去の分岐履歴が変わると、BTB エントリが別になる
○ 分岐履歴により、複数の分岐予測先を持てる
56
Branch History Buffer の挙動 (予測)
void bhb_update(uint58_t *bhb_state, unsigned long src,
unsigned long dst) {
*bhb_state <<= 2;
*bhb_state ^= (dst & 0x3f);
*bhb_state ^= (src & 0xc0) >> 6;
*bhb_state ^= (src & 0xc00) >> (10 - 2);
*bhb_state ^= (src & 0xc000) >> (14 - 4);
*bhb_state ^= (src & 0x30) << (6 - 4);
*bhb_state ^= (src & 0x300) << (8 - 8);
*bhb_state ^= (src & 0x3000) >> (12 - 10);
*bhb_state ^= (src & 0x30000) >> (16 - 12);
*bhb_state ^= (src & 0xc0000) >> (18 - 14);
}
57
余談: Return Stack Buffer (RSB)
call, ret 命令に特化したスタックバッファ
● call や ret も間接分岐命令だが...
● ret 命令は、直前の call の次の命令へジャンプ
○ call, ret は常にペアになっていることが多い
○ スタックへのアクセスは Load なのでとても遅い
● call 時に、アドレスをどこかに記憶しておけば...
○ => Return Stack Buffer
○ ret 命令後の投機的実行が高速に可能
○ 複数の return address をスタックでキャッシュする
58
余談: 現代の CPU の分岐予測器の進化
分岐予測器の進化は、最近でも続いている (というかアツい)
● 電力をそれほど消費せずに性能向上が可能
○ 分岐予測のミスは大きなペナルティ
○ ブランチ命令の先のパイプラインが止まってしまう
● ニューラルネットワーク分岐予測機
○ AMD の CPU? Intel も?
● 各社詳しい仕様は公開していない
○ 改良されたという事実のみが記載されている
○ 外側から観察して挙動を推測するのみ
59
Spectre / Meltdown の手法解説
60
Spectre
Variant 1
Bounds Check Bypass
61
Spectre Variant 1
分岐予測をコントロールして、実際とは異なる方向に予測させた
上で、メモリ領域を吸い出す
if (x < array1_size)
y = array2[array1[x] * 4096];
● 予め正規の x で何度か実行し、分岐予測を訓練させる
● x を悪意のあるもの (array1_size 以上など) に設定
○ array2 の領域の読み取りが投機的実行されてしまう
○ Flush + Reload により読み取り可能
62
Spectre Variant 1 - 大まかな流れ
cache_flush_array2(); // flush
for (N) read(training_x); // training
read(x + malicious_x); // attack
check_array2_latency(); // reload
---
void read(x) {
if (x < array1_size)
y = array2[array1[x] * 4096];
}
63
Spectre Variant 1 を利用した攻撃の影響範囲
実行中の権限で読めるすべてのデータを読み取り可能
● Intel 以外の CPU でも多く発生
● ページテーブルに乗っていれば何でも読める
○ マップされてない領域は読めない
○ 特権昇格は不可能
● コードを流し込めればいいので、うまくいくと...?
○ 仕組みが単純なので、色んな所に流し込める
○ カーネル空間に対して、eBPF という抜け道
○ JavaScript 上で Spectre できたら?
64
Spectre
Variant 2
Branch Target Injection
65
正直 Variant 2 は結構難しい...
66
Spectre Variant 2
BTB と分岐履歴をコントロールして、上手に特定のアドレスの命
令を投機的実行させる
● BTB は、コア内で共通である
● 同じコア内の違うプロセス・スレッドで、うまいこと BTB と分岐
履歴を訓練することができる
○ 例えば、SMT (HT) で動作している場合など
○ キーは、アドレス下位ビットが XOR 圧縮される
● その中でアクセスしたデータはキャッシュに残る
○ もしかしたらデータを抜き出せるかもしれない
○ もちろん、レジスタは破棄される
67
Spectre Variant 2 - BTB Injection
BTB・BHB をうまく騙して、共有させる
● まずは BTB・BHB を訓練
○ 投機的実行を騙して、実行させたいフローを再現
● BTB のエントリは、共有される
○ 簡単にコントロールすることができる
● 例えば Direct branch prediction BTB の場合
○ e.g. 0x100.0000 と 0x140.2000 はエントリが一緒
■ ※ BTB の解説ページ参照
■ 0x40.0000 と 0x2000 は XOR される
○ しかし、Direct branch BTB injection は成功率が低い?
68
Spectre Variant 2
うまいこと BHB を訓練させ、BTB を引く
● BHB は、過去の分岐履歴を保持する
● 分岐履歴を訓練させることは可能
○ また、BHB はうまくやると 0 の状態を作れる
■ BHB の挙動スライドを参照
■ このビット演算は XOR とシフトなので、反転してシフトしたものを流し込む
と 0 にすることが可能
○ その状態から、ジャンプ先アドレスを推測することも可能
● 訓練は、他のプロセスから行える
○ うまく攻撃対象のコードへ誘導できるかも
69
Meltdown
Rouge Cache Data Load
70
Meltdown - Spectre Variant 3
ユーザー権限で任意のメモリ領域の内容を推測可能な脆弱性
● このシリーズの中で最もリスクが高い (個人的な感想)
● カーネル空間に物理メモリがそのままマップされている
○ Linux (x86_64) の場合 0xffff880000000000
○ 通常は、ユーザーモードから読むことは不可能
● 投機的実行と CPU の特権レベルの関わりが影響
○ 特権レベルを無視して CPU が投機的実行を行う
○ 投機的実行を行ったあとに、特権違反により例外発生
○ キャッシュの内容は...?
○ Intel の CPU のみで発生
71
Meltdown の裏側 - 投機的実行
コードでは3行で説明できるぐらい簡単
● 裏で何が起きているのかを説明するのが難しい
...
raise_exception(); // 実際はここまで
// 以下の行には到達しない, でも OoO で実行されてる
access(probe_array[data * 4096]);
CPU の投機的実行は何を行うのだろう?
72
Meltdown の裏側 - 投機的実行
kernel_adder の内容を Flush+Reload で推測する例
raise_exception();
access(probe_array[*kernel_addr * 4096]);
投機的実行により、kernel_addr と proble_array の load が
raise_exception() より先に行われる
● 特権レベルの確認より先にメモリの読み取りが行われる
● proble_array を Flush + Reload で *kernel_addr を吸い出し
73
Meltdown の裏側 - 投機的実行
Meltdown のアセンブリの例
; rcx = kernel address
; rbx = probe array
retry:
mov al, byte [rcx] ; 実際はここで例外
shl rax, 0xc
jz retry
mov rbx, qword [rbx + rax]
74
仮想アドレス空間のメモリマップ - Linux x86_64
物理メモリすべての領域が 0xffff880000000000 からマップ
● ユーザー領域は仮想アドレス空間の先頭に配置
● 後半部分に、物理メモリすべてがストレートマップ
○ つまり、物理メモリに乗っていれば、すべての
ユーザー空間・カーネル空間の領域にアクセス可能
75
なぜ、物理メモリをすべてマップするのか?
システムコール・コンテキストスイッチの高速化のため
● ページテーブル切り替えのコストを削減
○ カーネル空間が別ページテーブルであれば...
■ システムコール毎に、ページテーブル切替 + TLB フラッシュ
○ カーネル空間のアドレスがページテーブル (プロセス) ごと
に異なるならば...
■ Global Page が使えない => カーネル空間の TLB が毎回消される
● TLB フラッシュは大きなコスト
○ ページウォーク => メモリアクセス
○ カーネル空間を常に TLB に載せておきたい
76
まとめ
77
Spectre / Meltdown まとめ
高度で複雑な CPU 高速化によって、思いがけない問題が
● 一般的なコンピューター全てで動作するという怖さ
○ 成功率・実現性などを考えたとしても、できる可能性がある
ということを、見逃してはいけない
● CPU を作っている人たちはどう思っていただろう?
○ 問題として捉えていたのかどうか
○ 自分だったら、見逃してしまっていたかも...
○ とはいえ、Meltdown は明らかに問題がある
● この後も、様々な変種が発見されていく...?
○ セキュリティ技術者にとってはチャンス?
78
Spectre Variant 1 実習編
79
演習コード URL: https://goo.gl/52P4UY
Spectre Variant 1 の PoC を作ってみよう
同じプログラム内の配列を直接読み出さずに Spectre する
● これまでの仕組みを利用して、”CPU を騙してみよう”
○ 理解してしまったら単純な仕組みだが...
○ 書いてみたらちょっと難しい
○ PoC を書いてみたからわかることも
● 何個か実装にあたって気をつけない点が
○ CPU の気持ちがわからないといけない
○ 投機的実行のデバッグはほぼ不可能
80
実装のコツ - Flush + Reload
● 必要な配列
char *secret; // 攻撃対象の文字列
uint8_t array1[適当な長さ]; // ダミー配列
uint8_t array2[256 * ???]; // Flush+Reload用
● 気をつけること: キャッシュライン・プリフェッチ
○ array1 と array2 の配置
○ array2 の長さ: char == 8bit == 256
■ それぞれの要素がキャッシュに乗る乗らないを判別するには?
81
実装のコツ - Flush
● Flush には Intrinsic を使う
○ 特殊なアセンブリを実行する組み込み関数
● その他利用すると便利な Intrinsics
○ #include <x86intrin.h>
○ _mm_clflush(addr)
■ 特定アドレスのキャッシュをフラッシュ
○ __rdtscp(&junk);
■ クロックサイクル単位の高精度タイムスタンプカウンタを読む
■ Reload で利用
○ _mm_mfence();
■ メモリフェンス命令
■ メモリアクセスが、必ずこの命令のあとで実行される
82
実装のコツ - Reload
メモリアクセスのレイテンシを読み取る例
int junk = 0;
uint64_t time1, time2;
time1 = __rdtscp(&junk);
junk = *addr;
time2 = __rdtscp(&junk) - time1;
● junk が最適化でいなくならないように
○ どこかでなにか使ってあげてください
● addr は、必ず volatile をつけること
○ addr のメモリを必ず読んで、レイテンシを測るため
83
実装のコツ - 分岐予測
if (x < array1_size)
y = array2[array1[x] * 4096];
● 正しい分岐を何度か成功させ学習させる
○ array1_size 以下の x を利用して “何度か” 実行
○ array1_size 以上の “非正規な” x を利用して実行
● コンパイラの最適化で消されないように
○ y は何も操作しない && ローカル変数では
○ y はメモリをいじるように volatile
○ なにか演算をさせたほうが良い
84
実装のコツ - 分岐予測
if (x < array1_size)
y = array2[array1[x] * 4096];
● 分岐予測 + 投機的なメモリアクセスが成功する条件
○ 分岐予測後メモリアクセスが完了するまでに、
条件分岐が実際に確定してはいけない
○ if の前に何らかの時間稼ぎが必要
● 時間稼ぎに使えそうなもの
○ 適当なメモリアクセス (条件を確定させないためには?
○ 適当な膨大な量の計算
○ OoO で Reorder・LOAD されることも考慮にいれて...
85
実装のコツ - 分岐予測
学習ループ内に条件分岐を生やすとうまく行かないことも
● 例えば、実際に攻撃対象の非正規 x を指定するときなど
● ループから出るのも条件が変わるので微妙
● 以下のような技も必要かも
// if (j % 6 == 0) x = malicious_x;
// else x = training_x;
x = ((j % 6) - 1) & ~0xFFFF;
x = (x | (x >> 16));
x = training_x ^ (x & (malicious_x ^ training_x));
86
Spectre Variant 1 の PoC を作ってみよう
すぐに終わって先に進みたい猛者たちに...
● コンパイラの最適化を有効にしてみよう
○ -O1 多分そのまま動く/ -O0 より成功率高い?
○ -O2, -O3 ちょっと考えないと難しい
● パラメーターをいじったりループ回数を変更して、投機的実行
の結果・CPU の動作を観察してみよう
○ 分岐予測の訓練回数を減らす・増やす
○ メモリアクセスを減らす・増やす
○ array2 のオフセットを減らす・増やす
87
Spectre / Meltdown 対策を考えてみよう
コンパイラ・CPU・カーネル... どんな対策でも OK
● Variant 1 ★☆☆
○ 境界値チェックが投機的実行されても安全な方法は?
○ パッチを当てて、確かめてみよう
● Variant 2 ★★★
○ 間接ブランチ命令を使わないでジャンプ...?
● Meltdown / Variant 3 ★★☆
○ ユーザー空間とカーネル空間の分離...?
※ 答えを知ってる人は、それを自分なりに説明してみよう
88
派生手法
89
Spectre Variant 4 (SpectreNG)
Foreshadow
Spectre Variant 4 (Spectre NG)
Speculative Store Bypass (投機的ストアバイパス)
https://blogs.technet.microsoft.com/srd/2018/05/21/analysis-and-mitigation-of-speculative-store-bypass-cve-2018-3639/
● LOAD 命令は投機的に実行される
● 手前にある STORE のアドレスが確定していなくても、LOAD
は先読みされる
mov [rdi+rcx],al
movzx r8,byte [rsi+rcx]
shl r8,byte 0xc
mov eax,[rdx+r8]
90
⇦ これはどうなる?
Spectre Variant 4 (Spectre NG)
STORE のアドレスが確定したあと...
● 以下の条件の場合、movzx が先に実行されてしまう
○ ”rdi” が未確定、“rsi” が確定済み
○ しかし、rsi と rdi が最終的に同じだったら?
mov [rdi+rcx],al
movzx r8,byte [rsi+rcx]
shl r8,byte 0xc
mov eax,[rdx+r8]
91
Foreshadow, Foreshadow-NG NEW!
L1 Terminal Fault を利用したメモリ内容の抜き取り
● ページテーブルエントリ (PTE) は P bit (present) のような、有
効か無効かという情報を保持している
○ もし、PTE が無効なら Terminal fault が発生
● Intel の CPU はこのような Fault が発生するとしても、先に L1
へアクセスしに行く
○ 無効な PTE を使うかも
○ 高速化のため
○ 結果は捨てるが...
○ Page walk の時間内に...
92
本当にわかる
Spectre と Meltdown 対策編
川田裕貴 @hktechno
93
セキュリティ・キャンプ全国大会2018
講義資料 URL: https://goo.gl/uTwX2c
PoC コード: https://goo.gl/Uozhy6
Spectre
Variant 1
Bounds Check Bypass
対策編
94
Spectre Variant 1 対策
array の index のビットをマスクする
● array の外側にアクセスできないようにすればいい
○ ブランチ命令はいくらでも学習可能なので使えない
○ 投機的実行が起きても問題ないように
● ビット演算のみで行う
○ パフォーマンス低下もそれほどではない
○ cmp size,index; sbb mask,mask; => x と AND
● Linux Kernel : array_index_nospec
○ https://github.com/torvalds/linux/blob/v4.17/include/linux/nospec.h
○ https://github.com/torvalds/linux/blob/v4.17/arch/x86/include/asm/barrier.h
95
Spectre Variant 1 対策
既存のもの (CPU, ソフトウェア) に対する対策はほぼ不可能
● CPU の microcode だけで治せるような問題ではない
● Intel 以外の CPU でも存在
○ 高速化のためにみんなやっていること
○ 問題があることにどのベンダーも気づいていなかった
○ 将来的にはどうなるのかまだわからない...
● ソフトウェア側では対応可能だが...
○ コンパイル済みのものは不可能
○ コンパイラが、コンパイル時に行うのも難しい
○ 手でコードを入れるしかない
96
Spectre Variant 1 対策
JavaScript や eBPF に対する対策
● 根本的な対策ではない
● JavaScript
○ JavaScript 上でも、仕組み的には Spectre できる
○ Timer の精度を下げると Spectre しづらくなる
● eBPF
○ 無効にする
○ Spectre に使われそうな部分に、きちんと対策をしておく
97
Spectre
Variant 2
Branch Target Injection
対策編
98
Spectre Variant 2 対策
Indirect branch が BTB を共有するのが問題だった
● ハードウェアによる対策 (microcode update)
○ 命令の追加: IBRS, IBPB, STIPB
○ IBRS: BTB の利用の抑制する命令
● Indirect branch を使わなければ良い
99
Spectre Variant 2 対策 - Retpoline
ROP のような手法を使って、投機的実行を騙す
ret は RSB (Return Stack Buffer) を利用して投機的実行する
call retpoline_call_target
2: /* 投機的実行はこっちに行く */
lfence /* stop speculation */
jmp 2b
retpoline_call_target:
lea 8(%rsp), %rsp /* スタック操作 */
ret /* 2: には戻らない */
100
Spectre Variant 2 対策 - Retpoline
しかし、Retpoline にも問題があることがわかった
● Return Stack Buffer (RSB) が枯渇したとき
○ BTB を引きにいく場合がある
○ どうも、Skylake 以降でこの修正が入った?
● うまいこと RSB を枯渇させられると、死んでしまう
101
Meltdown
Rouge Cache Data Load
対策編
102
Meltdown 対策
Kernel Page Table Isolation : KPTI
● カーネルページテーブルの分離
○ ユーザープロセスのページテーブルから、カーネル領域が
見えないように
○ Linux では KAISER という仕組みを実装
● パフォーマンス低下の問題
○ 最近の Intel CPU (Haswell 以降) では、緩和される
■ PCID のサポートの有無
○ 新しい Kernel + CPU を使っていれば問題ない
○ 古い CPU では、パフォーマンス低下が大きめ
103
TLB (Translation Lookaside Buffer) と
ページテーブルの関係
ページテーブルをキャッシュするものが TLB
● ページテーブルを引く (page walk) はとても遅い
● よくアクセスする領域は TLB にキャッシュする
○ ページテーブル切替時に、TLB はフラッシュされる
● KPTI を行うことで、カーネルを呼ぶたび TLB フラッシュ
○ とても遅くなる
104
これまで KPTI
User -> Kernel いらない TLB フラッシュ必要
プロセス切替 TLB フラッシュ必要 TLB フラッシュ必要
KPTI と PCID
PCID (Process Contex Identifers) により性能低下を抑制
● KPTI の問題: TLB が syscall でフラッシュされること
● プロセスごとに PCID を設定
● PCID を TLB にタグ付け、指定したタグだけフラッシュ
○ INVPCID 命令
○ PCID, INVPCID は、比較的最近 x86 に追加された機能
105
仮想アドレス タグ (PCID) 物理アドレス
0x10000000 100 0x00001000
0x10000000 200 0x00002000
0x20000000 100 0x00003000
PCID のメリット
PCID を導入すると、部分的に TLB フラッシュが可能
106
これまで KPTI KPTI + PCID
User -> Kernel いらない 全フラッシュ 一部フラッシュ
プロセス切替 全フラッシュ 全フラッシュ 一部フラッシュ
参考文献
https://spectreattack.com/
https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with
-side.html
https://blogs.technet.microsoft.com/srd/2018/05/21/analysis-and-mitigation-of-s
peculative-store-bypass-cve-2018-3639/
https://www.slideshare.net/mhiramat/spectrebusterslinuxspectre
https://speakerdeck.com/sat/tu-jie-dewakaruspectretomeltdown
http://mmi.hatenablog.com/entry/2018/02/02/003325
https://github.com/crozone/SpectrePoC
https://github.com/IAIK/meltdown
https://github.com/paboldin/meltdown-exploit
https://github.com/tbodt/spectre
https://foreshadowattack.eu/
107

Contenu connexe

Tendances

CTF for ビギナーズ ネットワーク講習資料
CTF for ビギナーズ ネットワーク講習資料CTF for ビギナーズ ネットワーク講習資料
CTF for ビギナーズ ネットワーク講習資料SECCON Beginners
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説murachue
 
CTF for ビギナーズ バイナリ講習資料
CTF for ビギナーズ バイナリ講習資料CTF for ビギナーズ バイナリ講習資料
CTF for ビギナーズ バイナリ講習資料SECCON Beginners
 
暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学MITSUNARI Shigeo
 
HamaCTF WriteUp (Unpack category)
HamaCTF WriteUp (Unpack category)HamaCTF WriteUp (Unpack category)
HamaCTF WriteUp (Unpack category)Shota Shinogi
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介MITSUNARI Shigeo
 
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説Takateru Yamagishi
 
初心者向けCTFのWeb分野の強化法
初心者向けCTFのWeb分野の強化法初心者向けCTFのWeb分野の強化法
初心者向けCTFのWeb分野の強化法kazkiti
 
型安全性入門
型安全性入門型安全性入門
型安全性入門Akinori Abe
 
いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門Fixstars Corporation
 
ctfで学ぼうリバースエンジニアリング
ctfで学ぼうリバースエンジニアリングctfで学ぼうリバースエンジニアリング
ctfで学ぼうリバースエンジニアリングjunk_coken
 
Wiresharkの解析プラグインを作る ssmjp 201409
Wiresharkの解析プラグインを作る ssmjp 201409Wiresharkの解析プラグインを作る ssmjp 201409
Wiresharkの解析プラグインを作る ssmjp 201409稔 小林
 
20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdfKazuaki Ishizaki
 
CyberChefの使い方(HamaCTF2019 WriteUp編)
CyberChefの使い方(HamaCTF2019 WriteUp編)CyberChefの使い方(HamaCTF2019 WriteUp編)
CyberChefの使い方(HamaCTF2019 WriteUp編)Shota Shinogi
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTtakesako
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門泰 増田
 
ゲーム木探索技術とコンピュータ将棋への応用
ゲーム木探索技術とコンピュータ将棋への応用ゲーム木探索技術とコンピュータ将棋への応用
ゲーム木探索技術とコンピュータ将棋への応用Shogo Takeuchi
 

Tendances (20)

CTF for ビギナーズ ネットワーク講習資料
CTF for ビギナーズ ネットワーク講習資料CTF for ビギナーズ ネットワーク講習資料
CTF for ビギナーズ ネットワーク講習資料
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
 
CTF for ビギナーズ バイナリ講習資料
CTF for ビギナーズ バイナリ講習資料CTF for ビギナーズ バイナリ講習資料
CTF for ビギナーズ バイナリ講習資料
 
暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学
 
HamaCTF WriteUp (Unpack category)
HamaCTF WriteUp (Unpack category)HamaCTF WriteUp (Unpack category)
HamaCTF WriteUp (Unpack category)
 
Lockfree Queue
Lockfree QueueLockfree Queue
Lockfree Queue
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介SSE4.2の文字列処理命令の紹介
SSE4.2の文字列処理命令の紹介
 
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
 
初心者向けCTFのWeb分野の強化法
初心者向けCTFのWeb分野の強化法初心者向けCTFのWeb分野の強化法
初心者向けCTFのWeb分野の強化法
 
型安全性入門
型安全性入門型安全性入門
型安全性入門
 
いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門いまさら聞けない!CUDA高速化入門
いまさら聞けない!CUDA高速化入門
 
How to run P4 BMv2
How to run P4 BMv2How to run P4 BMv2
How to run P4 BMv2
 
ctfで学ぼうリバースエンジニアリング
ctfで学ぼうリバースエンジニアリングctfで学ぼうリバースエンジニアリング
ctfで学ぼうリバースエンジニアリング
 
Wiresharkの解析プラグインを作る ssmjp 201409
Wiresharkの解析プラグインを作る ssmjp 201409Wiresharkの解析プラグインを作る ssmjp 201409
Wiresharkの解析プラグインを作る ssmjp 201409
 
20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf20221226_TITECH_lecture_ishizaki_public.pdf
20221226_TITECH_lecture_ishizaki_public.pdf
 
CyberChefの使い方(HamaCTF2019 WriteUp編)
CyberChefの使い方(HamaCTF2019 WriteUp編)CyberChefの使い方(HamaCTF2019 WriteUp編)
CyberChefの使い方(HamaCTF2019 WriteUp編)
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNT
 
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
 
ゲーム木探索技術とコンピュータ将棋への応用
ゲーム木探索技術とコンピュータ将棋への応用ゲーム木探索技術とコンピュータ将棋への応用
ゲーム木探索技術とコンピュータ将棋への応用
 

Similaire à 本当にわかる Spectre と Meltdown

バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1
バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1
バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1Hirotaka Kawata
 
Buffer overflow
Buffer overflowBuffer overflow
Buffer overflowionis111
 
Tremaで試すFirewall
Tremaで試すFirewallTremaで試すFirewall
Tremaで試すFirewallM Hagiwara
 
ASPLOS2017: Building Durable Transactions with Decoupling for Persistent Memory
ASPLOS2017: Building Durable Transactions with Decoupling for Persistent MemoryASPLOS2017: Building Durable Transactions with Decoupling for Persistent Memory
ASPLOS2017: Building Durable Transactions with Decoupling for Persistent MemoryAtsushi Koshiba
 
Meltdown を正しく理解する
Meltdown を正しく理解するMeltdown を正しく理解する
Meltdown を正しく理解するNorimasa FUJITA
 
Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊
Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊
Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊Supership株式会社
 
Statically detecting vulnerability under memory pressure using exhaustive search
Statically detecting vulnerability under memory pressure usingexhaustive searchStatically detecting vulnerability under memory pressure usingexhaustive search
Statically detecting vulnerability under memory pressure using exhaustive searchRuo Ando
 
Cpu idle expedition
Cpu idle expeditionCpu idle expedition
Cpu idle expeditioncota2n
 
OCaml でデータ分析
OCaml でデータ分析OCaml でデータ分析
OCaml でデータ分析Akinori Abe
 
リバースエンジニアリングのための新しいトレース手法 - PacSec 2010
リバースエンジニアリングのための新しいトレース手法 - PacSec 2010リバースエンジニアリングのための新しいトレース手法 - PacSec 2010
リバースエンジニアリングのための新しいトレース手法 - PacSec 2010Tsukasa Oi
 
ゼロから始める自作 CPU 入門
ゼロから始める自作 CPU 入門ゼロから始める自作 CPU 入門
ゼロから始める自作 CPU 入門Hirotaka Kawata
 
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきことmao999
 
Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)Fixstars Corporation
 
2021 03-09-ac ri-nngen
2021 03-09-ac ri-nngen2021 03-09-ac ri-nngen
2021 03-09-ac ri-nngen直久 住川
 
オープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステム
オープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステムオープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステム
オープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステムShinya Takamaeda-Y
 
Twitterのリアルタイム分散処理システム「Storm」入門
Twitterのリアルタイム分散処理システム「Storm」入門Twitterのリアルタイム分散処理システム「Storm」入門
Twitterのリアルタイム分散処理システム「Storm」入門AdvancedTechNight
 
2015 0228 OpenStack swift; GMO Internet Services
2015 0228 OpenStack swift; GMO Internet Services2015 0228 OpenStack swift; GMO Internet Services
2015 0228 OpenStack swift; GMO Internet ServicesNaoto Gohko
 
Linux/DB Tuning (DevSumi2010, Japanese)
Linux/DB Tuning (DevSumi2010, Japanese)Linux/DB Tuning (DevSumi2010, Japanese)
Linux/DB Tuning (DevSumi2010, Japanese)Yoshinori Matsunobu
 
Dbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publishDbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publishYohei Azekatsu
 
オペレーティングシステム 第1回-公開用
オペレーティングシステム 第1回-公開用オペレーティングシステム 第1回-公開用
オペレーティングシステム 第1回-公開用Ruo Ando
 

Similaire à 本当にわかる Spectre と Meltdown (20)

バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1
バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1
バイナリより低レイヤな話 (プロセッサの心を読み解く) - カーネル/VM探検隊@北陸1
 
Buffer overflow
Buffer overflowBuffer overflow
Buffer overflow
 
Tremaで試すFirewall
Tremaで試すFirewallTremaで試すFirewall
Tremaで試すFirewall
 
ASPLOS2017: Building Durable Transactions with Decoupling for Persistent Memory
ASPLOS2017: Building Durable Transactions with Decoupling for Persistent MemoryASPLOS2017: Building Durable Transactions with Decoupling for Persistent Memory
ASPLOS2017: Building Durable Transactions with Decoupling for Persistent Memory
 
Meltdown を正しく理解する
Meltdown を正しく理解するMeltdown を正しく理解する
Meltdown を正しく理解する
 
Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊
Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊
Graviton2プロセッサの性能特性と適用箇所/Supership株式会社 中野 豊
 
Statically detecting vulnerability under memory pressure using exhaustive search
Statically detecting vulnerability under memory pressure usingexhaustive searchStatically detecting vulnerability under memory pressure usingexhaustive search
Statically detecting vulnerability under memory pressure using exhaustive search
 
Cpu idle expedition
Cpu idle expeditionCpu idle expedition
Cpu idle expedition
 
OCaml でデータ分析
OCaml でデータ分析OCaml でデータ分析
OCaml でデータ分析
 
リバースエンジニアリングのための新しいトレース手法 - PacSec 2010
リバースエンジニアリングのための新しいトレース手法 - PacSec 2010リバースエンジニアリングのための新しいトレース手法 - PacSec 2010
リバースエンジニアリングのための新しいトレース手法 - PacSec 2010
 
ゼロから始める自作 CPU 入門
ゼロから始める自作 CPU 入門ゼロから始める自作 CPU 入門
ゼロから始める自作 CPU 入門
 
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと
 
Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)Fpga online seminar by fixstars (1st)
Fpga online seminar by fixstars (1st)
 
2021 03-09-ac ri-nngen
2021 03-09-ac ri-nngen2021 03-09-ac ri-nngen
2021 03-09-ac ri-nngen
 
オープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステム
オープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステムオープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステム
オープンソースコンパイラNNgenでつくるエッジ・ディープラーニングシステム
 
Twitterのリアルタイム分散処理システム「Storm」入門
Twitterのリアルタイム分散処理システム「Storm」入門Twitterのリアルタイム分散処理システム「Storm」入門
Twitterのリアルタイム分散処理システム「Storm」入門
 
2015 0228 OpenStack swift; GMO Internet Services
2015 0228 OpenStack swift; GMO Internet Services2015 0228 OpenStack swift; GMO Internet Services
2015 0228 OpenStack swift; GMO Internet Services
 
Linux/DB Tuning (DevSumi2010, Japanese)
Linux/DB Tuning (DevSumi2010, Japanese)Linux/DB Tuning (DevSumi2010, Japanese)
Linux/DB Tuning (DevSumi2010, Japanese)
 
Dbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publishDbts2012 unconference wttrw_yazekatsu_publish
Dbts2012 unconference wttrw_yazekatsu_publish
 
オペレーティングシステム 第1回-公開用
オペレーティングシステム 第1回-公開用オペレーティングシステム 第1回-公開用
オペレーティングシステム 第1回-公開用
 

Plus de Hirotaka Kawata

KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情
KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情
KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情Hirotaka Kawata
 
Micro Python で組み込み Python
Micro Python で組み込み PythonMicro Python で組み込み Python
Micro Python で組み込み PythonHirotaka Kawata
 
Introduction of PyCon JP 2014 in PyCon SG
Introduction of PyCon JP 2014 in PyCon SGIntroduction of PyCon JP 2014 in PyCon SG
Introduction of PyCon JP 2014 in PyCon SGHirotaka Kawata
 
自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会
自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会
自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会Hirotaka Kawata
 
産学間連携推進室(AC部屋) 2012 成果報告会
産学間連携推進室(AC部屋) 2012 成果報告会産学間連携推進室(AC部屋) 2012 成果報告会
産学間連携推進室(AC部屋) 2012 成果報告会Hirotaka Kawata
 
30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば
30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば
30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくばHirotaka Kawata
 
seccamp2012 チューター発表
seccamp2012 チューター発表seccamp2012 チューター発表
seccamp2012 チューター発表Hirotaka Kawata
 
Open Design Computer Project - Tsukuba.pm
Open Design Computer Project - Tsukuba.pmOpen Design Computer Project - Tsukuba.pm
Open Design Computer Project - Tsukuba.pmHirotaka Kawata
 
About University of Tsukuba Linux User Group
About University of Tsukuba Linux User GroupAbout University of Tsukuba Linux User Group
About University of Tsukuba Linux User GroupHirotaka Kawata
 

Plus de Hirotaka Kawata (10)

KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情
KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情
KotlinConf 2018 から見る 最近の Kotlin サーバーサイド事情
 
Micro Python で組み込み Python
Micro Python で組み込み PythonMicro Python で組み込み Python
Micro Python で組み込み Python
 
Introduction of PyCon JP 2014 in PyCon SG
Introduction of PyCon JP 2014 in PyCon SGIntroduction of PyCon JP 2014 in PyCon SG
Introduction of PyCon JP 2014 in PyCon SG
 
xv6 + mist32 + mruby
xv6 + mist32 + mrubyxv6 + mist32 + mruby
xv6 + mist32 + mruby
 
自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会
自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会
自作コンピューターでなんかする - 第八回 カーネル/VM探検隊&懇親会
 
産学間連携推進室(AC部屋) 2012 成果報告会
産学間連携推進室(AC部屋) 2012 成果報告会産学間連携推進室(AC部屋) 2012 成果報告会
産学間連携推進室(AC部屋) 2012 成果報告会
 
30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば
30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば
30日でできない!コンピューター自作入門 - カーネル/VM探検隊@つくば
 
seccamp2012 チューター発表
seccamp2012 チューター発表seccamp2012 チューター発表
seccamp2012 チューター発表
 
Open Design Computer Project - Tsukuba.pm
Open Design Computer Project - Tsukuba.pmOpen Design Computer Project - Tsukuba.pm
Open Design Computer Project - Tsukuba.pm
 
About University of Tsukuba Linux User Group
About University of Tsukuba Linux User GroupAbout University of Tsukuba Linux User Group
About University of Tsukuba Linux User Group
 

本当にわかる Spectre と Meltdown

  • 1. 本当にわかる Spectre と Meltdown 川田裕貴 @hktechno 1 セキュリティ・キャンプ全国大会2018 講義資料 URL: https://goo.gl/uTwX2c
  • 2. 自己紹介 @hktechno 川田 裕貴 (かわた ひろたか) ● LINE 株式会社 開発1センター LINE 開発1室 ○ メッセンジャープラットフォームの開発 ○ 主に、スタンプ・絵文字・着せかえ、たまに IoT ● 自作 CPU 開発にハマっていた ○ アウト・オブ・オーダー実行できる自作 CPU ○ 主にコンパイラを作っていた ○ http://open-arch.org/ ○ http://amzn.asia/gtThKbh 2
  • 3. 経歴? 2008年 セキュリティ・プログラミングキャンプ参加 2009, 2010, 2012 チューター, 2015 講師 2009年 筑波大学 情報学群 情報科学類 入学 2011年 IPA 未踏IT人材発掘・育成事業採択 (スーパークリエイターという謎の称号を頂く) 2014年 筑波大学大学院 コンピュータ・サイエンス専攻 入学 2016年 LINE 株式会社 入社 3
  • 4. 本日の進め方 ● CPU の基本的な仕組み: 60分 ○ パイプライン、キャッシュ、投機的実行、分岐予測 ○ Spectre / Meltdown の理解に必要な知識の説明 ● Spectre / Meltdown の説明: 30分 ● Spectre Variant 1 PoC を作ってみよう: 90分 ○ Spectre Variant 1 の対策を考えてみよう ○ Spectre Variant 1 の考えた対策を実装してみよう ● Meltdown の PoC デモとか: 10分 ● Spectre Variant 4 の説明: 10分 ○ Foreshadow の説明も...? 4
  • 6. Spectre と Meltdown について CPU を対象としたサイドチャネル攻撃に対する脆弱性 ● 2018年1月に Google の Project Zero などが共同で公開した 論文が発端 ○ https://meltdownattack.com/ ● オリジナルの手法は大きく分けて3つ (Variant 1,2,3) ○ Variant 3 のことを特に Meltdown と呼ぶ ● 現代の CPU アーキテクチャを巧妙に操ることにより、 サイドチャネル攻撃を可能に ○ 具体的には、メモリ内容の吸い取り 6
  • 7. Spectre / Meltdown の手法の概要 公開当初、大きく3つの手法が公表された https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html Code Name 通称 CVE Spectre Variant 1 Bounds Check Bypass CVE-2017-5753 Variant 2 Branch Target Injection CVE-2017-5715 Meltdown Variant 3 Rouge Cache Data Load CVE-2017-5754 7
  • 8. Spectre / Meltdown その後 その後、同じような脆弱性がいくつも発見されている... Code Name 通称 CVE Meltdown Variant 3a Rouge System Register Read CVE-2018-3640 SpectreNG Variant 4 Speculative Store Bypass CVE-2018-3639 Foreshadow (-NG) L1 Terminal Fault CVE-2018-3615 CVE-2018-3620 CVE-2018-3646 8
  • 9. Spectre / Meltdown の手法の概要 条件分岐予測の悪用 分岐予測をトレーニングし、 メモリアクセスの境界値 チェックをバイパスして実行 例えば... 本来は読み取りが行われ ないはずのメモリ領域の データを、吸い出すことが 可能。 Spectre Variant 2 間接分岐予測の悪用 間接分岐予測テーブルの 挙動を解析し、分岐予測を 同じキーになるアドレスでト レーニングした上で狙った コードを実行 例えば... 本来は読み取りが行われ ないはずのメモリ領域の データを、吸い出すことが 可能。 Meltdown (Variant 3) Out of order 実行の悪用 特権チェックバイパス 本来は例外が発生し実行さ れないコードを、特権レベ ルのチェックをバイパスして 実行 例えば... 本来は読み取ることができ ないメモリ領域のデータを、 吸い出すことが可能。 Spectre Variant 1 9
  • 10. サイドチャネル攻撃とは 攻撃対象に直接手を加えるのではなく、動作を観察することによ り、その挙動の違いから情報を得ようとする手法 ● Spectre, Meltdown においては、本来読み取れない領域にあ るメモリ内のデータを推測する手法 ○ 具体的には、メモリアクセスの時間の差を使う ● メモリ内のデータを ”直接” 読み取るわけではない ○ (実際は読み取っているわけだが... 詳細は後述) ● 元のプログラムに手を加える必要がなく、 攻撃を成功することができる (可能性がある) 10
  • 11. Meltdown と Spectre の難しい点 よく CPU アーキテクチャを理解していないと、理解困難 ● アセンブリが書けるだけでは、知識が足りない ● 繊細な CPU の気持ちを、よくわかっている必要がある ハードウェア由来の脆弱性、ソフトウェアでの対策が困難 ● 不可能ではないが、パフォーマンスの低下を伴う ● Spectre Variant 2 については、一部のアーキテクチャでは ハードウェア支援がないと対策がとても難しい 11
  • 12. サイドチャネル攻撃 - Flush + Reload メモリアクセスのレイテンシの差を利用した攻撃 1. キャッシュをクリア (特に、以下の array2 の領域) ○ キャッシュを追い出す (Eviction) させてもいい 2. メモリを間接的にアクセスさせる ○ y = array2[array[x] * 4096] ○ array[x] のメモリ内容を基に array2 にアクセスする ○ 4096 をかけるのは、プリフェッチを防ぐため 3. array2 のアクセスレイテンシを調べる ○ キャッシュに乗っていればレイテンシ低⇓ ○ 過去にアクセスされた可能性大 => 値がわかる 12
  • 13. 時間のかかる何か if (Array 境界値チェック) array1[array2[x]] その先の何か サイドチャネル攻撃 - Flush + Reload array2 攻撃対象 array1 CPU 時間かかるなぁ ここはよく true だから今回も true のときの 処理を投機的に実行だ! 間違ってたらあとでやりなおす! 13
  • 14. サイドチャネル攻撃 - Flush + Reload array2 攻撃対象 data[x] = 0xa array1 CPU 時間のかかる何か if (Array 境界値チェック) array1[array2[x]] その先の何か まだ終わってない x は確定していた! array[0xa] もアクセスできる! キャッシュに乗る! 準備完了 先に実行だ! 14
  • 15. サイドチャネル攻撃 - Flush + Reload array2 攻撃対象 data[x] = 0xa array1 CPU array1 を Flush 時間のかかる何か if (Array 境界値チェック) array1[array2[x]] その先の何か array1 を Reload array1 のキャッシュを消す array1 のアクセスレイテンシをチェック 過去にアクセスしたことがあれば速い。 レイテンシの差で、array2 のデータがわかる... 15
  • 19. 外の世界から見える CPU CPU の違いってなんだろう? ● 命令セットの違い ○ ARM vs x86 ○ 命令が増えた (e.g. AVX-512 とか...) ● クロック (2.4GHz, 3GHz, 4GHz…) ○ クロック数が変わってないから速度が変わらない? ● プロセス (14nm, 10nm, Tri-gate transistor…) ○ プロセスが細かくなったら何が良いの? CPU って中身どうなってるの??? 19
  • 20. 基本的な CPU パイプライン (MIST32 - In order) ● 大学の授業で出てきそうな構成? ○ Instruction Fetch ○ Instruction Buffer ○ Instruction Decode ○ Dispatch ○ Execution IB 20
  • 21. Intel Skylake の内部 これが現実 ⇨ ● 現代の CPU はとても複雑 ○ これでも単純化済 ● 命令の流れは一緒? ○ いろんなバッファ ○ 並列化 ● 日々進化する内部構造 /(^o^)\ よくわからない わかると楽しい\(^o^)/ 21
  • 22. Skylake の内部のパイプライン Skylake の詳細ブロック図 ● Frontend ○ フェッチ ○ デコーダ (x86 では重要) ● Execution Engine ○ 実行ユニット ○ 投機的実行の核心部 ● Memory Subsystem ○ データ用メモリアクセス部 22
  • 23. Intel x86 の内部的な命令: uOps (Micro-Ops) よく見る x86 の命令は、内部的な実行命令とは違う ● x86 アセンブラ1命令 => 複数の uOps ○ 最近は、逆の場合もある (Macro Fusion) ● なぜ命令を変換するのか? ○ x86 は CISC (命令がいろいろできるが複雑) ○ 時代・効率・回路規模は RISC ○ 設計が古いが、“互換性が重要” ○ 命令セットの設計に依存しない実行回路に ● Pentium III (P6) 以降に採用 23
  • 24. Intel x86 のフロントエンド デコーダが複雑 だが強い RISC に負けないように... ● 命令セットがクソ 設計が大変古い => uOps で解決 ○ デコーダが大変複雑 ○ 消費電力、回路規模に悪影響... ● デコーダをなるべく動かさない設計 ○ 1度デコードしたところは、再度デコードしない ○ uOp Cache (以前は Trace cache) ○ Loop stream detector 24
  • 25. Intel x86 における、マイクロコード (Microcode) ある1命令を、プログラム可能な uOps に変換する仕組み ● 利用目的 ○ ハードウェアの不具合 (エラッタ) の修正 ○ 複雑な命令の ● ハードウェアは基本的に修正パッチを当てられない ○ 出荷した後は不具合が修正不可能 ○ マイクロコードなら後からパッチを当てられる ● 利用例 ○ Haswell における TSX 無効化 ○ Spectre / Meltdown 対策における命令追加 25
  • 26. Intel x86 の実行ユニット 1コアの中に、複数の並列実行可能な実行ポート ● スーパースカラ + 投機的実行 ○ 投機的実行の詳細は後ほど詳しく ● Skylake では、実行ポートは8ポート ○ Haswell 以降増えた https://www.anandtech.com/show/6355/intels-haswell-architecture/8 ● ポートごとにできることが違う ○ ALU, FPU ○ Branch ○ Load / Store 26
  • 27. Intel x86 のキャッシュとメモリアクセス ● L1, L2, LLC (L3) の 3-Layer キャッシュ ○ L1 は Instruction, Data の2つのキャッシュ ● キャッシュラインは 64 byte ○ L1, L2: 8-way セットアソシアティブキャッシュ ○ 基本的にこの単位で、領域がキャッシュされる ● ページサイズは、基本的に 4096 byte ○ ページ境界を超えてプリフェッチはされない 27
  • 28. 現代の CPU と Meltdown / Spectre の関係 28
  • 29. Meltdown / Spectre がなぜ起きるのか 現代の CPU が命令を実行する際に行う、高度なハードウェアレ ベルでの最適化が原因 ● 特に、”投機的実行 (Speculative Execution)” と呼ばれる、命 令を先読みして高速化する手法が問題 CPU 内で行われる投機的実行の例 ● 命令のプリフェッチ ● ☆ アウト・オブ・オーダー実行 (順序を守らない実行) ● ☆ 分岐予測器を組み合わせた投機的実行 29
  • 30. “投機的” に命令列を実行して、間違っていたら結果を捨てる ● 書かれた命令の順番通りには実行しないこともある ○ 何もしないで待っているより、何かを実行 ○ とにかく、パイプラインに隙間が生まれないように ● 間違って、実際には実行しない命令を、先読みして実行してし まうことがある ○ 最終的な結果には影響が出ないように結果を捨てる ○ 現代の CPU では、間違うことはかなり少ない うまく使うと脆弱性につながる可能性が...? 現代の CPU における投機的実行 30
  • 31. Out of Order Execution アウト・オブ・オーダー実行 CPU の命令が上から順番に実行され るなんて、誰が言った! 31
  • 32. アウト・オブ・オーダー実行 (Out of Order Execution, OoO) 先読みした命令の依存関係を解析して、実行できる命令から実際 の記述順序を無視して先に実行する ● 命令間のレジスタ依存関係を動的に解析する ○ 例えば、即値ロード命令は他の命令と依存関係がない ○ 即値ロード命令の例: mov eax, 0x1 ● CPU は、複数の命令実行ポートを持っている ○ すべての命令が同時に実行完了するとは限らない ● 依存しているレジスタの結果が準備されたら、先に実行 ○ レジスタマッピングや、書き戻しのバッファリングにより整 合性を保つ 32
  • 33. アウト・オブ・オーダー実行の例 mov eax, [eax] xor ebx, ebx add ebx, eax inc ecx add eax, ecx Load (遅い) ↑の命令とは依存がない ↑の命令とは依存がない ※ 簡単な例で、特にアセンブリに意味はない 33
  • 34. アウト・オブ・オーダー実行の例 mov eax, [eax] xor ebx, ebx add ebx, eax inc ecx add eax, ecx 1 1 2 1 2 ⇓実行順 命令の順番を入れ替えても構わない しかも、開いてる実行ポートで並列に実行できる Load (遅い) ↑の命令とは依存がない 先に実行が可能 ↑の命令とは依存がない 先に実行が可能 34
  • 35. OoO に関する疑問・破壊する常識 ● あるレジスタ (eax や ebx) は CPU 内に1つじゃないの? ○ レジスタ・リネーミングを利用しています ○ 実はすべてただのエイリアスです ● CPU 内部で ”同時” に実行できる命令は1つじゃないの? ○ 複数の命令実行ポートが並列に並べられています ○ 例えば、Haswell 以降なら8実行ポート ● メモリアクセスを行う命令も並列に実行するの? ○ Intel の最近の CPU はメモリアクセスも OoO です ● なんでコンパイラでやらないの? ○ 成功していたら Itanium は死んでいない (VLIW) 35
  • 36. アウト・オブ・オーダー実行を支える技術 - 1 レジスタ・リネーミング (Register Renaming) ● 物理レジスタを、内部の仮想レジスタにリネームする ● 命令の依存をより少なくできる mov eax, [eax] inc eax mov [eax], eax mov eax, ebx mov eax, [eax] 同じ eax レジスタ しかし、依存はない 実は先に実行可能 36
  • 37. アウト・オブ・オーダー実行を支える技術 - 1 レジスタ・リネーミング (Register Renaming) ● 内部的なレジスタファイル内の物理レジスタと、論理レジスタ (eax, ebx…) との対応を持つ ○ 論理レジスタの数倍の物理レジスタを持っている mov r1, [r1] inc r1 mov [r1], r1 mov r3, r2 mov r3, [r3] レジスタ依存はない 実は先に実行可能 37
  • 38. レジスタ・リネーミング (Register Renaming) まとめると... ● Intel x86 のレジスタ少なすぎない? ○ どうせリネーミングするからあまり問題にならない ● 内部的なレジスタファイルの大きさ (Skylake) ○ Integer 180個 ○ Vector 168個 ● リネーミング時に依存性の排除を行う ○ 0 や 1 の即値やクリアは特殊な扱いをする場合も ○ Zeroing Idioms, Ones Idioms 38
  • 39. アウト・オブ・オーダー実行を支える技術 - 2 Tomasulo のアルゴリズム ● Reservation Station ○ 命令の依存関係を待つところ ○ ソースレジスタが利用可能かを監視して、 実行可能であれ ば実行を開始する ○ Unified な方式、実行ポートごとに持たせる方式 ● Re-order Buffer ○ 命令のコミットを制御するバッファ ○ 演算の結果は一度ここへ書かれる ※ 実際の Intel の CPU がこの通り実装されているわけではない 39
  • 40. Reservation Station の例 Reservation Station 命令1 SRC1 NG SRC2 OK 命令2 NG NG 命令3 OK NG 命令4 OK OK Reorder Buffer, Register File Execution Unit へ 40
  • 41. Reorder Buffer を使ったデータの流れ Reorder Buffer Stage 1. 割り当て Writeback Stage 2. 結果の書き込み Retire / Commit Reorder Buffer Register File 3. コミット ※ 実際の Intel の CPU がこの通り実装されているわけではない 41
  • 42. メモリアクセスのアウト・オブ・オーダー実行 レジスタとだいたい同じような仕組みで実装可能 ● メモリアクセスも順序は入れ替わる ○ LOAD 命令はできるものから先に実行 ○ 同じアドレスの LOAD は STORE より先に実行しない ○ Store Data の実行ポートは1つしかない ● 専用のバッファを持つ ○ 多分、専用のスケジューラを持っている ○ バッファのサイズ以上は先読みされない 42
  • 43. Reservation Station & Reorder Buffer まとめると... ● CPU 内部では Reorder Buffer と Reservation Station の仕 組みにより、命令の実行順序が入れ替わる ● メモリアクセスも順番が入れ替わることがある Skylake における実装 ● Reorder Buffer 224 エントリ ● Reservation Station 97 エントリ 43
  • 45. 分岐予測 (Branch Prediction) ブランチ命令で分岐する場合、行き先を予測して先に命令を読み 出し・実行を行う ● Intel x86 でいうとジャンプ系の命令 Jxx (jne, je, jz…) ● 分岐するかしないか (“Taken” or “Not Taken”) および、 その分岐先をハードウェアで動的に予測する ○ 基本的に、過去の分岐結果をもとに分岐先を予測する ○ CPU 内部に過去の分岐履歴を保存している ● 予測した分岐先の命令を先読みして、実行する ○ 現代の CPU では予測成功率 99% 以上 (個人的な感想) 45
  • 46. 分岐予測の例 - 簡単な分岐命令 mov eax, [eax] cmp eax, 0 je is_zero ... ... is_zero: ... ... 実行完了するまで Jump するかわからない Load (遅い) Block 1 Block 2 Block1 or Block2 どちらかを投機的に実行したいが、高 確率で正解を選びたい 基本的な if 文であれば PC 相対ジャンプであることが多い 46
  • 47. 分岐予測器ってどこにいるの フロントエンドにいる ● 分岐先アドレスを予測 ● ブランチ: CPU にとって大きなコスト ● 分岐ミス: 長いパイプラインが止まる ❌ 分岐予測成功したらラッキー ⭕ 分岐予測ミスは致命的 47
  • 48. 分岐予測器 (Branch Predictor) 古典的な実装 ● 分岐しない (or する) 方向で常に予測しておく ○ x86 だと Pentium 以前の CPU はこれ (i486 とか) ● 飽和カウンター (Bi-modal Counter) ○ 4つのステートを持って、1度ミスしただけでは予測方向が 変わらないような方式 ○ ループの分岐予測には有効 48
  • 49. 分岐予測器 - 最近の CPU の実装 いくつかの方式の組み合わせで、あらゆるパターンでも予測 ● 局所的分岐予測 ○ 同じアドレスの命令の、過去の分岐履歴を利用 ○ 同じアドレスで、同じパターンを繰り返す場合有用 ● グローバル分岐予測 ○ 過去のコア全体の分岐履歴のパターンをもとに、次の分岐 の挙動を予測 ● 上記の組み合わせを bit 演算で行い、予測テーブルを引く ○ PC + 局所履歴 + グローバル履歴 49
  • 50. 分岐予測の例 - 絶対アドレス間接ジャンプ (Indirect Branch) レジスタに格納されたアドレスにジャンプする場合 ● 例えばjXX [eax], call [eax] ● レジスタに格納されたアドレスが確定するまで、投機的実行が できない ○ これまでの手法は、PC 相対 + 即値ジャンプの場合 ○ 間接ジャンプで、特にLoad が噛むととても遅い ● 間接ジャンプ先も、実は予測可能では? ○ 同じ PC のジャンプは、よく同じアドレスにジャンプするので はないか? ※ PC (Program Counter): Intel では IP (Instruction Pointer) 実行中命令のアドレスのこと 50
  • 51. 間接分岐予測の概要 Intel x86 の最近の実装 Indirect Branch はちょっと特殊 アドレスだけでなく、history も 51
  • 52. Branch Target Buffer (BTB) ブランチ命令のジャンプ先アドレスを記憶しておくバッファ ● 同じ PC のジャンプは、よく同じ場所に飛ぶことが多い ○ 間接的 (Indirect branch) であっても同じ ○ これをキャッシュしておけば、投機的実行を実際のアドレス 確定前に行うことが可能 ● PC => ブランチ先 PC のバッファを持つ ○ 最近の Intel x86 CPU の場合... ○ PC を XOR で圧縮して、BTB のキーとする ○ キー内の一部の bit は他の bit と共有される ○ つまり、エントリを共有する場合がある 52
  • 53. BTB の構造 - Intel x86 Direct Branch Prediction 下位 31bit + XOR Folding でビット演算をして Lookup ● 下位 13 bit はそのまま ● 残りの 18bit に対して謎の XOR Folding を行う ○ 下記表参照 ⇨ ○ 規則性...? ● エントリの内容 ○ 下位32bitのアドレス ○ 4GB (2^32) を超えてジャンプ するかどうか ○ 上位 32bit のアドレス bit A bit B 0x40.0000 0x2000 0x80.0000 0x4000 0x100.0000 0x8000 0x200.0000 0x1.0000 0x400.0000 0x2.0000 0x800.0000 0x4.0000 0x2000.0000 0x10.0000 0x4000.0000 0x20.0000 53
  • 54. 余談: なぜ BTB は頑張ってビット幅を減らすのか 連想メモリ (CAM) は回路規模が厳しいから (多分) ● 大きな bit 列を比較 == 大きな規模のコンパレーター ○ エントリ数 * bit 幅に比例して大きくなる ○ 半分のビット幅にできたら、エントリが沢山入る ○ ただ、比較をするための回路でも馬鹿にできない ● ハードウェア上に大きな連想メモリを置くのは難しい ○ キャッシュ, TLB, BTB… ○ いろいろなものが、CAM の制限を受けている ○ 回路を作ってみるとわかるかも? 54
  • 55. BTB の概念と間接ジャンプの場合 分岐予測器はフロントエンドにいる ● パイプラインの隙間を開けない ○ ブランチを検知したら次のアドレスをすぐに確定 ○ 予測したブランチ先アドレスから、命令をフェッチ ○ 命令をデコードしたり、計算している暇はない ● レジスタを使った間接ジャンプでない場合は... ○ true or false で分岐先が確定 ○ 基本的に、分岐先を格納するものは1つで良い 間接ジャンプの場合は...? 分岐先の候補が複数あるかも? 55
  • 56. BTB の構造 - Intel x86 - Indirect Branch Prediction 複数ある分岐予測先を予測するために... ● Branch History Buffer (BHB) を使う ○ 分岐元下位ビットを Branch buffer state に利用 ○ Branch buffer state は、右シフトしつつ新しい情報が加え られる ● 過去の分岐履歴が変わると、BTB エントリが別になる ○ 分岐履歴により、複数の分岐予測先を持てる 56
  • 57. Branch History Buffer の挙動 (予測) void bhb_update(uint58_t *bhb_state, unsigned long src, unsigned long dst) { *bhb_state <<= 2; *bhb_state ^= (dst & 0x3f); *bhb_state ^= (src & 0xc0) >> 6; *bhb_state ^= (src & 0xc00) >> (10 - 2); *bhb_state ^= (src & 0xc000) >> (14 - 4); *bhb_state ^= (src & 0x30) << (6 - 4); *bhb_state ^= (src & 0x300) << (8 - 8); *bhb_state ^= (src & 0x3000) >> (12 - 10); *bhb_state ^= (src & 0x30000) >> (16 - 12); *bhb_state ^= (src & 0xc0000) >> (18 - 14); } 57
  • 58. 余談: Return Stack Buffer (RSB) call, ret 命令に特化したスタックバッファ ● call や ret も間接分岐命令だが... ● ret 命令は、直前の call の次の命令へジャンプ ○ call, ret は常にペアになっていることが多い ○ スタックへのアクセスは Load なのでとても遅い ● call 時に、アドレスをどこかに記憶しておけば... ○ => Return Stack Buffer ○ ret 命令後の投機的実行が高速に可能 ○ 複数の return address をスタックでキャッシュする 58
  • 59. 余談: 現代の CPU の分岐予測器の進化 分岐予測器の進化は、最近でも続いている (というかアツい) ● 電力をそれほど消費せずに性能向上が可能 ○ 分岐予測のミスは大きなペナルティ ○ ブランチ命令の先のパイプラインが止まってしまう ● ニューラルネットワーク分岐予測機 ○ AMD の CPU? Intel も? ● 各社詳しい仕様は公開していない ○ 改良されたという事実のみが記載されている ○ 外側から観察して挙動を推測するのみ 59
  • 60. Spectre / Meltdown の手法解説 60
  • 62. Spectre Variant 1 分岐予測をコントロールして、実際とは異なる方向に予測させた 上で、メモリ領域を吸い出す if (x < array1_size) y = array2[array1[x] * 4096]; ● 予め正規の x で何度か実行し、分岐予測を訓練させる ● x を悪意のあるもの (array1_size 以上など) に設定 ○ array2 の領域の読み取りが投機的実行されてしまう ○ Flush + Reload により読み取り可能 62
  • 63. Spectre Variant 1 - 大まかな流れ cache_flush_array2(); // flush for (N) read(training_x); // training read(x + malicious_x); // attack check_array2_latency(); // reload --- void read(x) { if (x < array1_size) y = array2[array1[x] * 4096]; } 63
  • 64. Spectre Variant 1 を利用した攻撃の影響範囲 実行中の権限で読めるすべてのデータを読み取り可能 ● Intel 以外の CPU でも多く発生 ● ページテーブルに乗っていれば何でも読める ○ マップされてない領域は読めない ○ 特権昇格は不可能 ● コードを流し込めればいいので、うまくいくと...? ○ 仕組みが単純なので、色んな所に流し込める ○ カーネル空間に対して、eBPF という抜け道 ○ JavaScript 上で Spectre できたら? 64
  • 66. 正直 Variant 2 は結構難しい... 66
  • 67. Spectre Variant 2 BTB と分岐履歴をコントロールして、上手に特定のアドレスの命 令を投機的実行させる ● BTB は、コア内で共通である ● 同じコア内の違うプロセス・スレッドで、うまいこと BTB と分岐 履歴を訓練することができる ○ 例えば、SMT (HT) で動作している場合など ○ キーは、アドレス下位ビットが XOR 圧縮される ● その中でアクセスしたデータはキャッシュに残る ○ もしかしたらデータを抜き出せるかもしれない ○ もちろん、レジスタは破棄される 67
  • 68. Spectre Variant 2 - BTB Injection BTB・BHB をうまく騙して、共有させる ● まずは BTB・BHB を訓練 ○ 投機的実行を騙して、実行させたいフローを再現 ● BTB のエントリは、共有される ○ 簡単にコントロールすることができる ● 例えば Direct branch prediction BTB の場合 ○ e.g. 0x100.0000 と 0x140.2000 はエントリが一緒 ■ ※ BTB の解説ページ参照 ■ 0x40.0000 と 0x2000 は XOR される ○ しかし、Direct branch BTB injection は成功率が低い? 68
  • 69. Spectre Variant 2 うまいこと BHB を訓練させ、BTB を引く ● BHB は、過去の分岐履歴を保持する ● 分岐履歴を訓練させることは可能 ○ また、BHB はうまくやると 0 の状態を作れる ■ BHB の挙動スライドを参照 ■ このビット演算は XOR とシフトなので、反転してシフトしたものを流し込む と 0 にすることが可能 ○ その状態から、ジャンプ先アドレスを推測することも可能 ● 訓練は、他のプロセスから行える ○ うまく攻撃対象のコードへ誘導できるかも 69
  • 71. Meltdown - Spectre Variant 3 ユーザー権限で任意のメモリ領域の内容を推測可能な脆弱性 ● このシリーズの中で最もリスクが高い (個人的な感想) ● カーネル空間に物理メモリがそのままマップされている ○ Linux (x86_64) の場合 0xffff880000000000 ○ 通常は、ユーザーモードから読むことは不可能 ● 投機的実行と CPU の特権レベルの関わりが影響 ○ 特権レベルを無視して CPU が投機的実行を行う ○ 投機的実行を行ったあとに、特権違反により例外発生 ○ キャッシュの内容は...? ○ Intel の CPU のみで発生 71
  • 72. Meltdown の裏側 - 投機的実行 コードでは3行で説明できるぐらい簡単 ● 裏で何が起きているのかを説明するのが難しい ... raise_exception(); // 実際はここまで // 以下の行には到達しない, でも OoO で実行されてる access(probe_array[data * 4096]); CPU の投機的実行は何を行うのだろう? 72
  • 73. Meltdown の裏側 - 投機的実行 kernel_adder の内容を Flush+Reload で推測する例 raise_exception(); access(probe_array[*kernel_addr * 4096]); 投機的実行により、kernel_addr と proble_array の load が raise_exception() より先に行われる ● 特権レベルの確認より先にメモリの読み取りが行われる ● proble_array を Flush + Reload で *kernel_addr を吸い出し 73
  • 74. Meltdown の裏側 - 投機的実行 Meltdown のアセンブリの例 ; rcx = kernel address ; rbx = probe array retry: mov al, byte [rcx] ; 実際はここで例外 shl rax, 0xc jz retry mov rbx, qword [rbx + rax] 74
  • 75. 仮想アドレス空間のメモリマップ - Linux x86_64 物理メモリすべての領域が 0xffff880000000000 からマップ ● ユーザー領域は仮想アドレス空間の先頭に配置 ● 後半部分に、物理メモリすべてがストレートマップ ○ つまり、物理メモリに乗っていれば、すべての ユーザー空間・カーネル空間の領域にアクセス可能 75
  • 76. なぜ、物理メモリをすべてマップするのか? システムコール・コンテキストスイッチの高速化のため ● ページテーブル切り替えのコストを削減 ○ カーネル空間が別ページテーブルであれば... ■ システムコール毎に、ページテーブル切替 + TLB フラッシュ ○ カーネル空間のアドレスがページテーブル (プロセス) ごと に異なるならば... ■ Global Page が使えない => カーネル空間の TLB が毎回消される ● TLB フラッシュは大きなコスト ○ ページウォーク => メモリアクセス ○ カーネル空間を常に TLB に載せておきたい 76
  • 78. Spectre / Meltdown まとめ 高度で複雑な CPU 高速化によって、思いがけない問題が ● 一般的なコンピューター全てで動作するという怖さ ○ 成功率・実現性などを考えたとしても、できる可能性がある ということを、見逃してはいけない ● CPU を作っている人たちはどう思っていただろう? ○ 問題として捉えていたのかどうか ○ 自分だったら、見逃してしまっていたかも... ○ とはいえ、Meltdown は明らかに問題がある ● この後も、様々な変種が発見されていく...? ○ セキュリティ技術者にとってはチャンス? 78
  • 79. Spectre Variant 1 実習編 79 演習コード URL: https://goo.gl/52P4UY
  • 80. Spectre Variant 1 の PoC を作ってみよう 同じプログラム内の配列を直接読み出さずに Spectre する ● これまでの仕組みを利用して、”CPU を騙してみよう” ○ 理解してしまったら単純な仕組みだが... ○ 書いてみたらちょっと難しい ○ PoC を書いてみたからわかることも ● 何個か実装にあたって気をつけない点が ○ CPU の気持ちがわからないといけない ○ 投機的実行のデバッグはほぼ不可能 80
  • 81. 実装のコツ - Flush + Reload ● 必要な配列 char *secret; // 攻撃対象の文字列 uint8_t array1[適当な長さ]; // ダミー配列 uint8_t array2[256 * ???]; // Flush+Reload用 ● 気をつけること: キャッシュライン・プリフェッチ ○ array1 と array2 の配置 ○ array2 の長さ: char == 8bit == 256 ■ それぞれの要素がキャッシュに乗る乗らないを判別するには? 81
  • 82. 実装のコツ - Flush ● Flush には Intrinsic を使う ○ 特殊なアセンブリを実行する組み込み関数 ● その他利用すると便利な Intrinsics ○ #include <x86intrin.h> ○ _mm_clflush(addr) ■ 特定アドレスのキャッシュをフラッシュ ○ __rdtscp(&junk); ■ クロックサイクル単位の高精度タイムスタンプカウンタを読む ■ Reload で利用 ○ _mm_mfence(); ■ メモリフェンス命令 ■ メモリアクセスが、必ずこの命令のあとで実行される 82
  • 83. 実装のコツ - Reload メモリアクセスのレイテンシを読み取る例 int junk = 0; uint64_t time1, time2; time1 = __rdtscp(&junk); junk = *addr; time2 = __rdtscp(&junk) - time1; ● junk が最適化でいなくならないように ○ どこかでなにか使ってあげてください ● addr は、必ず volatile をつけること ○ addr のメモリを必ず読んで、レイテンシを測るため 83
  • 84. 実装のコツ - 分岐予測 if (x < array1_size) y = array2[array1[x] * 4096]; ● 正しい分岐を何度か成功させ学習させる ○ array1_size 以下の x を利用して “何度か” 実行 ○ array1_size 以上の “非正規な” x を利用して実行 ● コンパイラの最適化で消されないように ○ y は何も操作しない && ローカル変数では ○ y はメモリをいじるように volatile ○ なにか演算をさせたほうが良い 84
  • 85. 実装のコツ - 分岐予測 if (x < array1_size) y = array2[array1[x] * 4096]; ● 分岐予測 + 投機的なメモリアクセスが成功する条件 ○ 分岐予測後メモリアクセスが完了するまでに、 条件分岐が実際に確定してはいけない ○ if の前に何らかの時間稼ぎが必要 ● 時間稼ぎに使えそうなもの ○ 適当なメモリアクセス (条件を確定させないためには? ○ 適当な膨大な量の計算 ○ OoO で Reorder・LOAD されることも考慮にいれて... 85
  • 86. 実装のコツ - 分岐予測 学習ループ内に条件分岐を生やすとうまく行かないことも ● 例えば、実際に攻撃対象の非正規 x を指定するときなど ● ループから出るのも条件が変わるので微妙 ● 以下のような技も必要かも // if (j % 6 == 0) x = malicious_x; // else x = training_x; x = ((j % 6) - 1) & ~0xFFFF; x = (x | (x >> 16)); x = training_x ^ (x & (malicious_x ^ training_x)); 86
  • 87. Spectre Variant 1 の PoC を作ってみよう すぐに終わって先に進みたい猛者たちに... ● コンパイラの最適化を有効にしてみよう ○ -O1 多分そのまま動く/ -O0 より成功率高い? ○ -O2, -O3 ちょっと考えないと難しい ● パラメーターをいじったりループ回数を変更して、投機的実行 の結果・CPU の動作を観察してみよう ○ 分岐予測の訓練回数を減らす・増やす ○ メモリアクセスを減らす・増やす ○ array2 のオフセットを減らす・増やす 87
  • 88. Spectre / Meltdown 対策を考えてみよう コンパイラ・CPU・カーネル... どんな対策でも OK ● Variant 1 ★☆☆ ○ 境界値チェックが投機的実行されても安全な方法は? ○ パッチを当てて、確かめてみよう ● Variant 2 ★★★ ○ 間接ブランチ命令を使わないでジャンプ...? ● Meltdown / Variant 3 ★★☆ ○ ユーザー空間とカーネル空間の分離...? ※ 答えを知ってる人は、それを自分なりに説明してみよう 88
  • 89. 派生手法 89 Spectre Variant 4 (SpectreNG) Foreshadow
  • 90. Spectre Variant 4 (Spectre NG) Speculative Store Bypass (投機的ストアバイパス) https://blogs.technet.microsoft.com/srd/2018/05/21/analysis-and-mitigation-of-speculative-store-bypass-cve-2018-3639/ ● LOAD 命令は投機的に実行される ● 手前にある STORE のアドレスが確定していなくても、LOAD は先読みされる mov [rdi+rcx],al movzx r8,byte [rsi+rcx] shl r8,byte 0xc mov eax,[rdx+r8] 90 ⇦ これはどうなる?
  • 91. Spectre Variant 4 (Spectre NG) STORE のアドレスが確定したあと... ● 以下の条件の場合、movzx が先に実行されてしまう ○ ”rdi” が未確定、“rsi” が確定済み ○ しかし、rsi と rdi が最終的に同じだったら? mov [rdi+rcx],al movzx r8,byte [rsi+rcx] shl r8,byte 0xc mov eax,[rdx+r8] 91
  • 92. Foreshadow, Foreshadow-NG NEW! L1 Terminal Fault を利用したメモリ内容の抜き取り ● ページテーブルエントリ (PTE) は P bit (present) のような、有 効か無効かという情報を保持している ○ もし、PTE が無効なら Terminal fault が発生 ● Intel の CPU はこのような Fault が発生するとしても、先に L1 へアクセスしに行く ○ 無効な PTE を使うかも ○ 高速化のため ○ 結果は捨てるが... ○ Page walk の時間内に... 92
  • 93. 本当にわかる Spectre と Meltdown 対策編 川田裕貴 @hktechno 93 セキュリティ・キャンプ全国大会2018 講義資料 URL: https://goo.gl/uTwX2c PoC コード: https://goo.gl/Uozhy6
  • 94. Spectre Variant 1 Bounds Check Bypass 対策編 94
  • 95. Spectre Variant 1 対策 array の index のビットをマスクする ● array の外側にアクセスできないようにすればいい ○ ブランチ命令はいくらでも学習可能なので使えない ○ 投機的実行が起きても問題ないように ● ビット演算のみで行う ○ パフォーマンス低下もそれほどではない ○ cmp size,index; sbb mask,mask; => x と AND ● Linux Kernel : array_index_nospec ○ https://github.com/torvalds/linux/blob/v4.17/include/linux/nospec.h ○ https://github.com/torvalds/linux/blob/v4.17/arch/x86/include/asm/barrier.h 95
  • 96. Spectre Variant 1 対策 既存のもの (CPU, ソフトウェア) に対する対策はほぼ不可能 ● CPU の microcode だけで治せるような問題ではない ● Intel 以外の CPU でも存在 ○ 高速化のためにみんなやっていること ○ 問題があることにどのベンダーも気づいていなかった ○ 将来的にはどうなるのかまだわからない... ● ソフトウェア側では対応可能だが... ○ コンパイル済みのものは不可能 ○ コンパイラが、コンパイル時に行うのも難しい ○ 手でコードを入れるしかない 96
  • 97. Spectre Variant 1 対策 JavaScript や eBPF に対する対策 ● 根本的な対策ではない ● JavaScript ○ JavaScript 上でも、仕組み的には Spectre できる ○ Timer の精度を下げると Spectre しづらくなる ● eBPF ○ 無効にする ○ Spectre に使われそうな部分に、きちんと対策をしておく 97
  • 98. Spectre Variant 2 Branch Target Injection 対策編 98
  • 99. Spectre Variant 2 対策 Indirect branch が BTB を共有するのが問題だった ● ハードウェアによる対策 (microcode update) ○ 命令の追加: IBRS, IBPB, STIPB ○ IBRS: BTB の利用の抑制する命令 ● Indirect branch を使わなければ良い 99
  • 100. Spectre Variant 2 対策 - Retpoline ROP のような手法を使って、投機的実行を騙す ret は RSB (Return Stack Buffer) を利用して投機的実行する call retpoline_call_target 2: /* 投機的実行はこっちに行く */ lfence /* stop speculation */ jmp 2b retpoline_call_target: lea 8(%rsp), %rsp /* スタック操作 */ ret /* 2: には戻らない */ 100
  • 101. Spectre Variant 2 対策 - Retpoline しかし、Retpoline にも問題があることがわかった ● Return Stack Buffer (RSB) が枯渇したとき ○ BTB を引きにいく場合がある ○ どうも、Skylake 以降でこの修正が入った? ● うまいこと RSB を枯渇させられると、死んでしまう 101
  • 102. Meltdown Rouge Cache Data Load 対策編 102
  • 103. Meltdown 対策 Kernel Page Table Isolation : KPTI ● カーネルページテーブルの分離 ○ ユーザープロセスのページテーブルから、カーネル領域が 見えないように ○ Linux では KAISER という仕組みを実装 ● パフォーマンス低下の問題 ○ 最近の Intel CPU (Haswell 以降) では、緩和される ■ PCID のサポートの有無 ○ 新しい Kernel + CPU を使っていれば問題ない ○ 古い CPU では、パフォーマンス低下が大きめ 103
  • 104. TLB (Translation Lookaside Buffer) と ページテーブルの関係 ページテーブルをキャッシュするものが TLB ● ページテーブルを引く (page walk) はとても遅い ● よくアクセスする領域は TLB にキャッシュする ○ ページテーブル切替時に、TLB はフラッシュされる ● KPTI を行うことで、カーネルを呼ぶたび TLB フラッシュ ○ とても遅くなる 104 これまで KPTI User -> Kernel いらない TLB フラッシュ必要 プロセス切替 TLB フラッシュ必要 TLB フラッシュ必要
  • 105. KPTI と PCID PCID (Process Contex Identifers) により性能低下を抑制 ● KPTI の問題: TLB が syscall でフラッシュされること ● プロセスごとに PCID を設定 ● PCID を TLB にタグ付け、指定したタグだけフラッシュ ○ INVPCID 命令 ○ PCID, INVPCID は、比較的最近 x86 に追加された機能 105 仮想アドレス タグ (PCID) 物理アドレス 0x10000000 100 0x00001000 0x10000000 200 0x00002000 0x20000000 100 0x00003000
  • 106. PCID のメリット PCID を導入すると、部分的に TLB フラッシュが可能 106 これまで KPTI KPTI + PCID User -> Kernel いらない 全フラッシュ 一部フラッシュ プロセス切替 全フラッシュ 全フラッシュ 一部フラッシュ