CPUモードと特権リング・保護の仕組み
なぜユーザーアプリはカーネルメモリを壊せないのか。x86のリング0〜3と特権命令、ページ保護、SMEP/SMAPまで原理で押さえ、現代OSが2リングに集約した設計判断まで腑に落とせます。
- 1.x86はCS下位2ビットのCPL(リング0〜3)で特権を表し、HLTやCR3書き換えなど特権命令はCPLが0でないと一般保護例外で弾かれます。
- 2.保護はセグメント(DPL/RPLとCPLの比較)とページ(PTEのU/SビットとR/W)の二層で効き、現代OSは前者を無効化に近づけ後者を主力にしています。
- 3.SMEPはring0からのユーザーページ実行を、SMAPはユーザーページのアクセスを禁じ、リング2・3を使わないのは設計判断と移植性のためです。
特権はどこに保持されるのか
「カーネルモードとユーザーモード」という2層の話はカーネルモードとユーザーモードで扱いましたが、x86 のハードウェアが実際に管理しているのは2段ではなく 4段 の特権レベル、いわゆるリング0〜3です。リング0が最高特権(カーネル)、リング3が最低特権(アプリ)で、数字が小さいほど強い、という非直感的な向きに注意してください。
現在の特権レベルは独立したフラグではなく、コードセグメントセレクタ(CS レジスタ)の下位2ビット に符号化されます。これを CPL(Current Privilege Level)と呼びます。CPU は命令を1つ実行するたびにこの CPL を参照し、その操作が許されるかを判定します。CPL は任意に書き換えられるものではなく、後述するゲート(割り込み・syscall など)を通った時だけ正規の手順で変わります。これが「勝手に特権を上げられない」ことの正体です。
特権命令と一般保護例外
リングが効く第一の場面が 特権命令 です。システム全体に影響する命令は CPL が0でないと実行できません。代表例を挙げます。
| 命令 | 作用 | CPL≠0 で実行すると |
|---|---|---|
| HLT | CPUを停止状態へ | #GP(一般保護例外) |
| MOV CRn | CR0/CR3等の制御レジスタ操作 | #GP |
| LGDT/LIDT | 記述子テーブルの再設定 | #GP |
| INVLPG | TLBエントリの無効化 | #GP |
| WRMSR | MSR(モデル固有レジスタ)書込 | #GP |
ユーザーコードがこれらを実行しようとすると、CPU は処理を行わず #GP(General Protection Fault、ベクタ13) を発生させ、制御はカーネルの例外ハンドラへ移ります。Linux ならカーネルが SIGSEGV や SIGILL に変換してプロセスへ届けます。重要なのは、判定がソフトウェアのチェックではなく 命令デコード時にハードウェアが行う 点です。だからユーザーは「うっかり」でもカーネル専用命令を実行できません。
割り込み禁止の CLI/STI や I/Oポート命令(IN/OUT)は完全な特権命令ではなく、RFLAGS 内の IOPL(I/O Privilege Level)フィールドと CPL の比較で許可されます。CPL が IOPL 以下なら実行可、という条件付きの中間的な扱いです。ただし現代 OS は IOPL をほぼ常に0に保つため、実質ユーザーからは使えません。
保護の二層:セグメントとページ
リングはメモリ保護でも働きますが、x86 には保護機構が 2層 存在し、両方を通過したアクセスだけが成立します。
第一層が セグメント保護 です。各セグメント記述子は DPL(Descriptor Privilege Level)を持ち、データやコードへアクセスする際に「CPL と、セレクタの RPL(Requested Privilege Level)のうち弱いほう」が DPL 以下かを比較します。数値が大きいほど弱い ため、CPL 3 のコードは DPL 0 のカーネルセグメントへ届きません。RPL は呼び出し側が「自分の権限で代理アクセスする」ことを示す仕組みで、カーネルがユーザーから渡されたポインタを自分の特権で素通しさせない安全弁です。
第二層が ページ保護 です。各ページテーブルエントリ(PTE)は U/S ビット(User/Supervisor)と R/W ビットを持ちます。U/S が S(Supervisor)のページは CPL 3 から触れず、R/W が0なら書き込めません。アドレス変換そのものの仕組みは仮想記憶のアドレス変換とMMU/TLBの内部を参照してください。
ユーザー命令によるメモリ読み書き
1) セグメント検査: max(CPL,RPL) ≤ DPL か → 失敗なら #GP
2) ページ検査: U/S と R/W が CPL/操作種別に整合か → 失敗なら #PF
両方を満たして初めてアクセス成立
セグメント違反は #GP、ページ違反は #PF(Page Fault、ベクタ14)と、原因によって例外が分かれます。
なぜ2リングに集約されたのか
ハードウェアは4リングを提供するのに、Linux も Windows も リング0とリング3しか使いません。リング1・2は事実上死蔵です。これは性能や偶然ではなく、明確な設計判断です。
| 観点 | 4リング全活用 | 2リング集約(現実) |
|---|---|---|
| 移植性 | x86以外(ARM/RISC-V)は2段が主流で対応が割れる | 2段ならどのISAでも素直に対応 |
| 保護の主力 | セグメントDPLに依存 | ページU/Sビットに一本化でき単純 |
| 64bitモード | ロングモードはセグメント保護を実質無効化 | セグメントに頼れないので2層化が必然 |
| 設計の複雑さ | 中間リングのゲート設計が増える | 境界が1本で検証しやすい |
決定打は x86-64 のロングモード です。64ビットモードではセグメントのベースとリミットが無効化され(フラットメモリ)、セグメント保護はほぼ機能しません。残るのは CPL とページの U/S ビットだけで、U/S は「Supervisor か User か」の 1ビット=2状態 しか区別できません。つまりハードウェア自身が、メモリ保護のレベルでは2リングしか表現しなくなったのです。リング1・2に意味のあるページ保護を与える術がないため、使う理由が消えました。
歴史的には OS/2 がデバイスドライバをリング2に置き、初期の Xen は準仮想化ゲストのカーネルをリング1で走らせました(ホストはリング0)。後者はゲストカーネルから特権命令を取り上げる古典的手法でしたが、ハードウェア仮想化(VT-x/AMD-V)が リングとは直交する新しい次元 を導入して不要になりました。詳細はハードウェア仮想化拡張の内部(VT-x/AMD-V)を参照してください。
SMEP と SMAP:境界をさらに固める
2リングに集約しても残る攻撃面があります。カーネル(ring0)のバグを突いて、ユーザー空間に仕込んだコードやデータをカーネル権限で実行・参照させる 攻撃です。リングだけでは防げないため、後付けの保護が CR4 レジスタのビットとして追加されました。
- SMEP(Supervisor Mode Execution Prevention):ring0 から U/S=User のページにある命令を 実行 しようとすると #PF で停止します。攻撃者がユーザー領域に置いたシェルコードへカーネルの命令ポインタを飛ばす ret2usr 系攻撃を無効化します。
- SMAP(Supervisor Mode Access Prevention):ring0 から User ページを 読み書き すると #PF になります。ただしカーネルは正当な理由でユーザーバッファを触る必要があるため、
STAC/CLAC命令で一時的に許可します(copy_to_userなどの内部はこの窓を開閉しています)。
STAC で AC フラグを立てた区間が長すぎたり、例外で抜けて閉じ忘れたりすると、その間カーネルはユーザー制御下のメモリを無防備に参照できてしまいます。copy_from_user のような関数が許可窓を最小区間に閉じ込めるのはこのためで、許可の粒度が保護強度を直接左右します。
これらは「特権を持つ側ほど、低特権側のメモリを安易に触らない」という原則をハードウェアで強制するもので、リング機構の弱点を補う最後の砦です。syscall 命令によるリング遷移そのものの内部はシステムコールのABIと呼び出し機構の内部で扱っています。
まとめ
x86 の特権は CS 下位2ビットの CPL(リング0〜3)に保持され、特権命令の可否は命令デコード時にハードウェアが判定して CPL≠0 なら #GP を出します。メモリ保護はセグメント(DPL/RPL と CPL の比較、#GP)とページ(U/S・R/W ビット、#PF)の二層で、64ビットのロングモードがセグメント保護を無効化したため保護はページ U/S の1ビットに一本化されました。これが移植性と相まって OS を2リングへ集約させた設計判断です。残る ring0 からのユーザー領域参照は SMEP(実行禁止)と SMAP(アクセス禁止、STAC/CLAC で開閉)が塞ぎます。
OS Article
CPUモードと特権リング・保護の仕組みを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
特権リング
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
保護はセグメント(DPL/RPLとCPLの比較)とページ(PTEのU/SビットとR/W)の二層で効き、現代OSは前者を無効化に近づけ後者を主力にしています。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「特権リング / 保護モード」に近いか確認する。
- 強みである「x86はCS下位2ビットのCPL(リング0〜3)で特権を表し、HLTやCR3書き換えなど特権命令はCPLが0でないと一般保護例外で弾かれます。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。