TL

投機実行とサイドチャネル(Spectre/Meltdown)のOS対策

性能のために投機実行したCPUが、捨てたはずの結果をキャッシュに痕跡として残す。MeltdownとSpectreの原理から、KPTI・retpoline・IBRSなどOSの緩和策とそのコストまで筋道立てて理解できます。

応用投機実行SpectreMeltdownKPTIサイドチャネルCPU脆弱性最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.投機実行は分岐確定前に命令を先走り実行する。結果はロールバックされるが、投機中にアクセスしたデータがキャッシュに残り、キャッシュタイミング測定で値を漏らす。
  • 2.Meltdownは権限チェックより先に投機ロードが進む実装欠陥で、KPTI(カーネルページテーブルをユーザー空間から分離)で塞ぐ。Spectreは分岐予測器を汚染して被害者コードに投機ロードさせる攻撃。
  • 3.Spectre対策はretpoline(間接分岐を予測不能なret迂回に置換)やIBRS/IBPB(予測器を隔離するMSR)で、いずれもCPUサイクルや権限切替コストを上乗せする。

投機実行はなぜ「捨てた結果」を漏らすのか

現代のCPUは、分岐の行き先や条件が確定する前から後続命令を投機実行します。予測が当たれば前倒しで仕事が進み、外れれば投機結果をロールバックして何事もなかったかのように戻します。アーキテクチャ状態(レジスタやメモリの可視値)から見れば、捨てられた投機の痕跡は残らない――というのが設計上の建前です。

問題は、ロールバックがアーキテクチャ状態しか巻き戻さない点にあります。投機実行中に発生したマイクロアーキテクチャ状態の変化、とりわけキャッシュへのデータ充填は元に戻りません。あるアドレスに投機的に触れると、そのキャッシュラインが残り、後でそのアドレスへのアクセスが速いか遅いかを測れば「投機中に触れたか」が分かります。これが投機実行サイドチャネルの核心で、捨てたはずの値をタイミングという裏口から読み出します。

測定の骨子(Flush+Reload)
1. probe[256][4096] を用意し、全ラインをキャッシュから追い出す(flush)
2. 何らかの方法で「秘密の値 v」を投機中に取得させ、probe[v*4096] を読ませる
3. 投機はロールバックされるが probe[v*4096] のラインはキャッシュに残る
4. probe[i*4096] を i=0..255 で順に読み、最速だった i が秘密の値 v

ページサイズ刻み(4096)で配列を引くのは、プリフェッチや隣接ラインの巻き込みを避け、1値が1ラインに対応するようにするためです。

Meltdown:権限チェックを投機が追い越す

Meltdown(Rogue Data Cache Load)は、一部CPUの実装欠陥を突きます。ユーザーモードからカーネルアドレスをロードすると本来は保護違反ですが、該当CPUでは権限チェックの結果が確定する前に、投機的なロードがキャッシュからデータを読み、そのバイトを使った後続のメモリアクセス(前述のprobe参照)まで投機実行してしまいます。

// ユーザーから実行(kernel_addr は本来アクセス不可)
char v = *(char*)kernel_addr;   // 投機中に値が取れてしまう
temp = probe[v * 4096];         // v に依存したラインがキャッシュに残る
// この後で例外が確定し命令列はロールバックされるが、痕跡は残る

例外(保護違反)は最終的に発生しますが、それは命令がリタイアする段階での話で、投機段階のキャッシュ汚染には間に合いません。攻撃者は例外をハンドリングまたは抑止しつつ、Flush+Reloadでカーネルメモリを1バイトずつ吸い出せます。カーネル/ユーザーモードの境界がアーキテクチャ上は守られていても、マイクロアーキテクチャ上は素通りされていたわけです。

KPTI:ページテーブルそのものを分離する

ソフトウェア対策の決定版がKPTI(Kernel Page Table Isolation)です。原理は単純で、漏らせない情報をそもそもユーザーモードのページテーブルに載せない。従来はカーネルとユーザーが1つのアドレス空間を共有し、カーネル領域を「特権ページ」属性で隠していましたが、Meltdownはその属性チェックを投機で迂回しました。KPTIはユーザー実行時に使うページテーブルからカーネルのマッピングを物理的に取り除くため、投機ロードしようにも変換できる物理アドレスが存在しないのです。

KPTIのコストはどこに乗るか

KPTIではユーザー用とカーネル用の2組のページテーブルを持ち、システムコールや割り込みでカーネルへ入るたびにCR3(ページテーブルベース)を切り替えます。CR3の書き換えは原則TLBフラッシュを伴うため、TLB再充填コストが境界越えごとに乗ります。PCID/ASID対応CPUではフラッシュを抑えられ、影響はワークロード次第で数%程度に収まりますが、システムコール多発系では無視できません。

Spectre:分岐予測器を「教育」して撃たせる

SpectreはMeltdownと別系統で、CPUの実装欠陥というより投機実行という機構そのものを悪用します。攻撃者は自分のメモリを読むのではなく、被害者コードを誤った投機経路へ誘導し、被害者の権限で秘密に触れさせます。

  • Variant 1(境界チェックの迂回 / BCB): if (i < arr_len) { y = arr2[arr1[i] * 4096]; } のような境界チェック付きコードで、予測器に「条件は真」と学習させた後、わざと範囲外のiを渡す。CPUはチェック確定前に投機実行し、範囲外のarr1[i]を使ったロードでキャッシュを汚す。
  • Variant 2(分岐ターゲット注入 / BTI): 間接分岐(関数ポインタ呼び出し等)の予測先を格納する**BTB(分岐ターゲットバッファ)**を攻撃者が訓練し、被害者の間接分岐を攻撃者の選んだ「ガジェット」へ投機的に飛ばす。

被害者自身の権限で動くため、KPTIのようなアドレス空間分離では防げません。対策は予測の流れ自体に介入します。

retpoline:間接分岐を予測不能にする

retpoline(return + trampoline)は、Variant 2を狙ったコンパイラ/カーネルの対策です。jmp *%raxのような間接分岐を、ret命令を使った定型コードへ置換し、BTBによる投機先注入を無効化します。

; 元の間接呼び出し  call *%rax  を以下に置換
  call set_up_target          ; 戻りアドレスをスタックへ
capture_speculation:
  pause                       ; 投機が走っても
  lfence                      ;   ここで足踏みさせる
  jmp capture_speculation
set_up_target:
  mov %rax, (%rsp)            ; 本当の飛び先で戻りアドレスを上書き
  ret                         ; ret は RSB を使い、誤予測先は無害なループ

要は間接分岐をすべてretに変換し、CPUがretの飛び先を予測する際に使う**RSB(リターンスタックバッファ)**へ安全な値を仕込むことで、攻撃者が訓練したBTBの予測先が使われないようにします。投機が暴走しても無害なpause; lfenceループに閉じ込められます。

IBRS/IBPB/STIBP:ハードウェアで予測器を隔離する

CPUベンダはマイクロコード更新でMSR(モデル固有レジスタ)による制御を追加しました。OSはこれらを適切な契機で書き込みます。

機構意味と効果OSが使う契機
IBRS特権上昇後、下位特権の訓練した予測を使わせないカーネル入口で有効化
IBPB予測器の状態に障壁を置き履歴を一掃するプロセス切替時に発行
STIBP兄弟ハイパースレッド間の予測器共有を遮断信頼境界をまたぐ実行時

これらは強力ですが高コストです。とくに初期IBRSはカーネル入口ごとに書き込むと重く、後に「予測を境界越しに使わせない」状態を保つEnhanced IBRSが導入され、入口ごとの書き込みが不要になりました。IBPBはプロセス切替のたびに予測器を捨てるため、切替直後は分岐予測が外れやすくなる代償があります。

MDS系:キャッシュ以外の通り道

Spectre/Meltdown以降、ラインフィルバッファやストアバッファといったCPU内部の一時バッファから投機的にデータが漏れるMDS(Microarchitectural Data Sampling)系も判明しました。対策はVERW命令でカーネルからユーザーへ戻る際にバッファをフラッシュする方式が中心で、これもまた境界越えごとのコストとして積み上がります。

コストと運用:何をどこまで効かせるか

緩和は一律ではなく、脅威モデルとハードウェア世代で要否が変わります。新しいCPUはMeltdownを設計段階で修正済み(権限チェックを投機ロードより前に効かせる)なので、KPTIを無効化しても安全な場合があります。逆にハイパースレッドを共有するクラウドのマルチテナント環境では、STIBPやコアスケジューリング(信頼境界の異なるスレッドを同一物理コアに同居させない)まで踏み込む必要があります。

試験・面接のポイント

「ロールバックされるのはアーキテクチャ状態だけで、キャッシュ等のマイクロアーキテクチャ状態は戻らない」が全体の鍵。MeltdownはKPTIで防ぐ実装欠陥、Spectreは予測器汚染でretpoline/IBRSが必要、の対応関係を取り違えないこと。KPTIのコストはCR3切替に伴うTLBフラッシュであり、PCID/ASIDが効くと覚える。

まとめ

  • 投機実行はアーキテクチャ状態をロールバックするが、キャッシュ充填などのマイクロアーキテクチャ状態は残り、タイミング測定で秘密を漏らす。
  • Meltdownは権限チェックを投機ロードが追い越す実装欠陥で、KPTIがカーネルのマッピングをユーザー用ページテーブルから外して塞ぐ(コストはCR3切替=TLBフラッシュ)。
  • Spectreは分岐予測器を汚染し被害者の権限で投機ロードさせる攻撃で、retpoline(間接分岐の無害化)やIBRS/IBPB/STIBP(予測器の隔離)で緩和する。
  • いずれの対策もCPUサイクル・権限切替・予測精度の形でコストを払う。脅威モデルとCPU世代に応じて取捨選択するのが実務の判断軸。

仕組みの土台としてカーネル/ユーザーモードMMU/TLBの内部も押さえておくと、各緩和策がどこに効いているかが立体的に見えてきます。

OS Article

投機実行とサイドチャネル(Spectre/Meltdown)のOS対策を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

投機実行

比較で見る軸

難易度: advanced / カテゴリ: OS / タグ数: 6

導入後に効く点

Meltdownは権限チェックより先に投機ロードが進む実装欠陥で、KPTI(カーネルページテーブルをユーザー空間から分離)で塞ぐ。Spectreは分岐予測器を汚染して被害者コードに投機ロードさせる攻撃。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
OS
タグ数
6

判断チェックリスト

  • 自社の用途が「投機実行 / Spectre」に近いか確認する。
  • 強みである「投機実行は分岐確定前に命令を先走り実行する。結果はロールバックされるが、投機中にアクセスしたデータがキャッシュに残り、キャッシュタイミング測定で値を漏らす。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

投機実行SpectreMeltdownKPTIサイドチャネル投機実行SpectreMeltdown
参考: 公式情報