このページの目次
カーネルの堅牢化
対応範囲: KASLR → stack protector → KPTI → KASAN/KMSAN/UBSAN → FORTIFY_SOURCE → lockdown → seccomp → SLAB_FREELIST_RANDOM → init_on_free カーネルバージョン: 2.6 ~ 6.x
アドレス空間のランダム化: KASLR
起動時にカーネルコード/データ/モジュールのベースアドレスをランダム化:
→ ROP/JOP攻撃ではgadgetsのアドレスが必要 → ベースアドレスが不明 → 攻撃が困難
実装: arch/x86/boot/compressed/kaslr.c
ブートローダーの段階でランダムなベースアドレスを選択 → 再配置されたカーネルを解凍
kptr_restrict: /proc/kallsymsからのカーネルアドレス漏洩を制限
/proc/sys/kernel/kptr_restrict = 2 → 非rootユーザーからはすべて0として表示
Stack Protector
// CONFIG_STACKPROTECTOR: コンパイラが各関数のスタックフレームにcanaryを挿入
// gcc: -fstack-protector-strong
// → 保護対象: ローカル配列がある関数や、アドレス演算を行う関数
// canaryが書き換えられると → スタックオーバーフロー → __stack_chk_fail() → panic
// kernel/stackleak.c (オプション): より徹底的なスタッククリア
// 各システムコールの返却前に使用済みスタック領域を消去
// → スタック情報の漏洩防止(未初期化のローカル変数など)
KPTI: Meltdown対策
2018年のMeltdown脆弱性:
CPUは予測実行中にページテーブルのU/S bitをチェックしない → ユーザーモードからカーネルメモリを読み取れる
KPTI (Kernel Page Table Isolation):
各プロセスに2つのページテーブルを持つ:
カーネルモード: 完全マッピング (user + kernel)
ユーザーモード: 最小限 (user + trampoline)
→ ユーザーモードではカーネルメモリへの予測アクセスができない
パフォーマンスへの影響: ~5%~30% (システムコール集中型), PCIDにより一部緩和
現代のCPU (Cascade Lake以降, Zen2以降): ハードウェアで修正済み、KPTIは自動的に無効化
Sanitizers: 実行時エラー検出
// KASAN (Kernel Address Sanitizer):
// 検出: 境界外アクセス, use-after-free, double-free
// シェードメモリに基づく (コンパイル時のインストルメンテーション)
// オーバーヘッド: ~1.5-2倍の低速化, デバッグ用カーネルのみ
// KMSAN (Kernel Memory Sanitizer):
// 検出: 未初期化メモリへのアクセス
// KASANよりも重い (~3倍), 開発用途のみ
// UBSAN (Undefined Behavior Sanitizer):
// 検出: 整数オーバーフロー, シフトの境界外, 空ポインタの逆参照
// オーバーヘッドが低く、本番環境で有効化可能 (CONFIG_UBSAN_BOUNDS)
FORTIFY_SOURCE
// コンパイル時にmemcpy/strcpy/sprintfを置換:
// バウンダリ情報がある場合 → チェック付き呼び出しを生成
// コンパイル時に既知の境界外アクセス → コンパイルエラー
// 実行時の境界外アクセス → BUG() (エラーを返さない)
//
// ユーザー空間のFORTIFY_SOURCEと類似しているが、より多くの関数をカバー
Kernel Lockdown
UEFI Secure Bootが有効化されると:
→ kernel lockdownが自動的に有効化される (integrityモード)
→ 禁止される操作:
- /dev/mem, /dev/kmem (メモリ読み書き)
- ioportへのアクセス
- kexec (署名されていないカーネルの読み込み)
- BPFによるカーネルメモリへの書き込み
- ACPIテーブルの上書き
/proc/sys/kernel/lockdown:
none / integrity / confidentiality
seccomp
// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)
// プロセスが呼び出せるシステムコールを制限 (不可逆)
// BPFフィルター:
// システムコール番号と引数をチェック → ALLOW / DENY / KILL / TRAP
// Chromium, Docker, systemdなどが使用
// SECCOMP_RET_USER_NOTIF (5.0以降):
// 判断をカーネル内で行わず、ユーザー空間の監視プロセスに通知
// 例: コンテナマネージャが特定のシステムコールの承認/拒否を行う
その他の堅牢化オプション
# 解放時にゼロクリア:
CONFIG_INIT_ON_FREE_DEFAULT_ON=y # 解放されたslabオブジェクトをゼロクリア (UAFによる情報漏洩防止)
# slab freelistのランダム化:
CONFIG_SLAB_FREELIST_RANDOM=y # slab freelistポインタをランダムにソート (ヒープエクスプロイト防止)
# ユーザーメモリコピーのハードニング:
CONFIG_HARDENED_USERCOPY=y # usercopyのソース/ターゲットが有効なheap/slab領域内であることを検証
# スタックのランダム化 (既存, プロセス単位):
参考
- ソースコード:
security/lockdown/,kernel/seccomp.c,mm/kasan/,lib/ubsan.c - カーネルドキュメント:
Documentation/admin-guide/kernel-parameters.txt - LWN: "Kernel address space layout randomization", "The kernel lockdown patches"
キーワード: KASLR, stack protector, KPTI, KASAN, FORTIFY_SOURCE, lockdown, seccomp, init_on_free