● foo(x)を呼ぶと… 分岐予測の流れ: 分岐予測成功の場合 27 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) … (a) ret = a[x]; .. (b) Return ret; } コード 1. 流れbが先行 a. len(not on cache)をロード中… b. A[x](on cache)をロード
● foo(x)を呼ぶと… 分岐予測の流れ: 分岐予測成功の場合 28 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) … (a) ret = a[x]; .. (b) Return ret; } コード 1. 流れbが先行 a. len(not on cache)をロード中… b. A[x](on cache)をロード完了 2. 流れaが完了。分岐予測成功 a. Lenをロード完了 -> “x(=10) < len(=100)”がtrue b. retにa[x]をストア 3. a, bの順にリタイア
● foo(x)を呼ぶと… 分岐予測の流れ: 分岐予測成功の場合 29 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) … (a) ret = a[x]; .. (b) Return ret; } コード 1. 流れbが先行 a. len(not on cache)をロード中… b. A[x](on cache)をロード完了 2. 流れaが完了。分岐予測成功 a. Lenをロード完了 -> “x(=10) < len(=100)”がtrue b. retにa[x]をストア完了 3. a, bの順にリタイアしてから先に進む
● X = 1000とすると… 分岐予測の流れ: 分岐予測失敗の場合 31 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) ... (a) ret = a[x]; … (b) Return ret; } コード 1. 流れbが先行 a. lenをロード中… b. A[x]をロード
● X = 1000とすると… 分岐予測の流れ: 分岐予測失敗の場合 32 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) ... (a) ret = a[x]; … (b) Return ret; } コード 1. 流れbが先行 a. lenをロード中… b. A[x]をロード完了 2. 流れaが完了。分岐予測失敗を検出 a. Lenをロード -> “x(=1000) < len(=100)”がfalse b. retにa[x]をストア中…
● X = 1000とすると… 分岐予測の流れ: 分岐予測失敗の場合 33 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) ... (a) ret = a[x]; … (b) Return ret; } コード 1. 流れbが先行 a. lenをロード中… b. A[x]をロード完了 2. 流れaが完了。分岐予測失敗を検出 a. Lenをロード完了 -> “x(=1000) < len(=100)”がfalse b. retにa[x]をストア中… 3. 流れbの実行を捨てて先に進む
● foo(1000)を呼ぶと… 分岐予測が外れた場合 37 Char a[100]; Int len = 100; Void foo(x) { Int ret = -1; If (x < len) …(a) ret = probe[a[x] * PAGE_SIZE]; …(b) Return ret; } 1. 流れbが先行 a. Lenをロード中… b. A[x]をロード -> Probe[a[x] * PAGE_SIZE] をロード 2. 流れaにおいて分岐予測失敗を検出 a. Lenをロード -> “x(=1000) < len(=100)”は false b. ret にprobe[...]をストア中…
Variant 1を利用したFlush+Reload攻撃 ● できること a. Fooの引数変更によって、攻撃対象プロセスがアクセス可能な任意のメモリを読み出せる b. カーネル内で実行すればカーネル内のメモリも読み出せる ● 処理の流れ a. 初期化: Probe[]をキャッシュフラッシュ b. 教育: foo()を何度も呼び出して if文におけるCPUの分岐予測先をtrueにする c. 抜き取り: foo()を実行してif文の分岐予測を失敗させる -> 取りたいデータをprobe[]に抜く d. 復元 40
Variant 3を利用したFlush+Reload攻撃の例 ● メインルーチン a. SIGSEGVのシグナルハンドラを登録 b. setjmp()。登録時は処理cへ。longjmp()による復帰時は処理 eへ c. 初期化: probe[]をnot on cacheへ d. 抜き取り。SIGSEGV発生 e. 復元 ● SIGSEGVハンドラ a. longjmp()でメインルーチンの処理 bへ 50