eBPFとカーネルの拡張機構
カーネルを再ビルドせず、再起動もせずに振る舞いを足せる。検証器が安全を保証し、JITが実速度で走る、その仕組みを原理から解き明かします。
- 1.eBPFはカーネル内の小さな仮想マシンで動くプログラム。検証器がロード時に静的解析で安全性を保証し、JITが機械語へ変換してネイティブ速度で実行する。
- 2.kprobe・tracepoint・XDPなどのフックにアタッチし、イベント発生時に呼ばれる。マップ(map)を介してカーネルとユーザー空間がデータを共有する。
- 3.観測(トレース)・ネットワーク(XDPでの高速処理)・セキュリティ(LSM/seccomp)に応用され、モジュールより安全に機能を足せる。
カーネルを「安全に」拡張するという難題
OS の振る舞いを変えたいとき、従来の選択肢は2つでした。カーネルモジュールで機能を足すか、カーネル本体を改造して再ビルドするか。だがモジュールは カーネルモード で生のポインタを触れるため、バグ1つでシステム全体が落ちます。再ビルドは再起動を伴い、本番では現実的ではありません。
eBPF(extended Berkeley Packet Filter)は、この「拡張したいが壊したくない」というジレンマに、サンドボックス化された小さな仮想マシンで答えます。ユーザーが書いたプログラムをカーネル内で動かしつつ、ロード時の静的検証で危険なものを門前払いする。これが核心です。
eBPF 仮想マシンとJITコンパイル
eBPF プログラムは、専用の RISC 風命令セット を持つ仮想マシン(VM)向けにコンパイルされます。レジスタは R0-R10 の11本(64ビット幅)で、R10 は読み取り専用のスタックフレームポインタ、R1-R5 は呼び出し引数、R0 は戻り値、という ABI が定められています。命令は固定長で、算術・分岐・ロード/ストア・ヘルパ関数呼び出しなどに限られます。
ソース(C/Rust)
│ clang -target bpf
▼
eBPFバイトコード ──→ [検証器] ──通過──→ [JIT] ──→ ネイティブ機械語
│
拒否(EINVAL等)
実行方式は2段構えです。まずバイトコードをインタプリタで動かすこともできますが、現代の本番カーネルでは JIT(Just-In-Time)コンパイラ がバイトコードを x86-64 や arm64 のネイティブ命令へ変換 します。eBPF レジスタは物理レジスタへ素直にマップでき、変換後は通常の関数とほぼ同等の速度で走ります。「VM だから遅い」のではなく、検証を通った後はネイティブ実行というのが要点です。
ただし VM の能力は意図的に絞られています。任意のループは原則禁止(後述の検証器が停止性を保証できないため)、命令数に上限があり、呼べるのはヘルパ関数という安全な API 群だけ。生のカーネル関数を好き勝手には呼べません。この「不自由さ」こそが安全性の対価です。
検証器(verifier)が安全性をどう保証するか
eBPF の信頼性の中心は 検証器 です。プログラムをロードすると、実行する前にカーネルが静的解析で安全性を証明しようとします。証明できなければロードは失敗し、1命令も動きません。
検証器が行う主な検査は次のとおりです。
- 到達可能な全経路の探索:分岐を分木として辿り、すべてのパスを走査する。証明できないパスが1つでもあれば拒否。
- メモリアクセスの境界検査:スタックやマップ、パケットバッファへのアクセスが、必ず確保済みの範囲内であることを各経路で確認する。配列風アクセスの前には範囲チェックの分岐が必須。
- レジスタの型・値域の追跡:各レジスタが「スカラか、特定オブジェクトへのポインタか」を追跡し、値の取り得る範囲(min/max)も記録する。未初期化レジスタの読み出しは禁止。
- 停止性の保証:無制限ループを許さないことで、プログラムが必ず有限ステップで終わることを保証する。
初期の eBPF はループを一切許さず、固定回数の繰り返しは展開(unroll)するしかありませんでした。現在は 有界ループ(bounded loop) が認められています。検証器がループ変数の値域を追跡し、反復回数の上限を静的に確定できる 場合に限り通すからです。上限が証明できないループは依然として拒否されます。停止性を犠牲にしないための線引きです。
検証は経路ごとの状態(各レジスタの型と値域、スタックの状態)を辿る抽象解釈に近く、状態が爆発しないよう、等価な状態は枝刈り(プルーニング)されます。それでも複雑なプログラムでは検証コスト自体が問題になり得るため、命令数・複雑度の上限が設けられています。
検証器が保証するのは「カーネルをクラッシュさせない・無限ループしない・不正メモリを触らない」という安全性であって、プログラムの論理的正しさではありません。境界チェックを満たすために書いた if が、意図と違う値を通してしまうことはあります。安全に動くことと、正しく動くことは別物です。
どこにアタッチするか:kprobe・tracepoint・XDP
eBPF プログラムは単体では動かず、フック(アタッチポイント)に結び付けられて初めて、対応するイベント発生時に呼ばれます。代表的なフックを役割で整理します。
| フック | 発火点 | 安定性 | 主な用途 |
|---|---|---|---|
| kprobe / kretprobe | 任意のカーネル関数の入口/出口 | 低(関数名・引数に依存) | 動的トレース・デバッグ |
| tracepoint | カーネルに静的に埋め込まれた計測点 | 高(ABIとして維持) | 安定した観測 |
| XDP | NICドライバ直後(最も早い受信地点) | — | 高速パケット処理・DDoS防御 |
| tc (traffic control) | ネットワークスタックのqdisc層 | — | 送受信両方のパケット制御 |
| LSM | カーネルのセキュリティフック | — | アクセス制御・ポリシー強制 |
kprobe は任意のカーネル関数に動的に仕掛けられる柔軟さが魅力ですが、関数名や引数は内部実装でありバージョン間で変わり得ます。対して tracepoint はカーネルが意図的に置いた計測点で、ABI として安定が約束されるため、長期運用の観測には tracepoint が好まれます。仕掛けの土台はブレークポイント命令などの 割り込み機構 で、関数入口で実行を一瞬奪い eBPF を呼びます。
XDP(eXpress Data Path) は性質が異なります。パケットが NIC ドライバに届いた最速の地点——カーネルがソケットバッファ(skb)を割り当てる前——でフックします。ここでパケットを XDP_DROP(破棄)・XDP_PASS(通常処理へ)・XDP_TX(送り返す)・XDP_REDIRECT(別 NIC へ)と裁けるため、スタックを通さずにライン速度でフィルタリングでき、DDoS 防御やロードバランサに使われます。
マップ:カーネルとユーザー空間をつなぐ
eBPF プログラムは状態を持てず、呼び出しのたびにスタックも消えます。ではどうやって統計を貯め、設定を受け取るのか。答えが マップ(map) です。
マップはカーネル側に置かれる キー・バリュー型のデータ構造で、ハッシュマップ・配列・リングバッファ・LRU・per-CPU 版など多様な型があります。
- eBPF プログラム側:ヘルパ関数でマップを読み書きし、カウンタ更新やデータ収集を行う。
- ユーザー空間側:システムコール経由でマップを読み、結果を取得したり設定を流し込んだりする。
このマップが、サンドボックス内のプログラムと外の世界をつなぐ唯一の正規ルートです。per-CPU マップを使えばロック競合を避けて集計でき、リングバッファを使えばイベントを低オーバーヘッドでユーザー空間へストリーミングできます。なお、ユーザー空間からのロードや問い合わせは専用の bpf() システムコール 1本に集約されています。
三大応用:観測・ネットワーク・セキュリティ
eBPF の応用は大きく3領域に分かれます。
- 観測(オブザーバビリティ):kprobe/tracepoint にアタッチして、関数呼び出し回数・遅延・スタックトレースをカーネルを止めずに収集する。bcc や bpftrace、本番診断ツール群がこの上に立ち、従来の「printf を足して再ビルド」を不要にしました。
- ネットワーク:XDP/tc でパケットを直接裁く。Cilium のようなコンテナネットワークは、サービス間通信のロードバランスやポリシー適用を eBPF で実装し、名前空間と cgroups ベースの隔離に高速なデータ経路を与えます。
- セキュリティ:LSM フックにアタッチして、ファイルアクセスやプロセス実行を動的なポリシーで許可/拒否する。seccomp と組み合わせ、システムコールの可否を柔軟に判定する用途もあります。
やりたいことがカーネルモジュールでも書けるとき、なお eBPF が選ばれるのは、検証器という安全網があるからです。モジュールのバグはパニック直結ですが、eBPF はロード時点で危険なものが弾かれ、最悪でもそのプログラムが動かないだけ。再起動不要で着脱でき、本番に生きたまま仕掛けられる——この運用上の安全性が決定的です。
「eBPF はなぜ安全にカーネルを拡張できるのか」に一言で答えられるかが分かれ目です。核は (1) サンドボックス VM で動かす、(2) ロード時に検証器が全経路を静的解析して安全性を証明する、(3) 通った後は JIT でネイティブ速度 の三段構え。tracepoint は ABI 安定、kprobe は柔軟だが不安定、XDP は skb 割り当て前の最速地点、という対比も頻出です。
まとめ
eBPF は「カーネルを壊さずに拡張する」を、検証器による静的な安全性証明とJIT によるネイティブ実行の組み合わせで実現します。プログラムは絞られた命令セットの VM 上で動き、無制限ループや任意のメモリアクセスは検証器が拒否。kprobe/tracepoint で観測点に、XDP/tc でネットワーク最前線に、LSM でセキュリティフックにアタッチし、マップを介してユーザー空間とデータを交換します。応用は観測・ネットワーク・セキュリティの三本柱。土台にあるのは 割り込み機構 と システムコール、そして カーネルモードとユーザーモード の境界——この境界を、安全網付きで越えさせるのが eBPF の本質です。
OS Article
eBPFとカーネルの拡張機構を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
eBPF
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
kprobe・tracepoint・XDPなどのフックにアタッチし、イベント発生時に呼ばれる。マップ(map)を介してカーネルとユーザー空間がデータを共有する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「eBPF / Linux」に近いか確認する。
- 強みである「eBPFはカーネル内の小さな仮想マシンで動くプログラム。検証器がロード時に静的解析で安全性を保証し、JITが機械語へ変換してネイティブ速度で実行する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。