本页目录

Live Patching

覆盖: kernel livepatch (KLP) → ftrace-based → atomic replace → 状态一致性模型 → 实时内核 内核版本: 4.0 ~ 6.x (主线), 企业版 (RHEL 7+)

概述

Live patching 允许在不重启内核的情况下修复内核函数——适用于关键安全修复 (CVE) 和小逻辑 bug。核心原理:在正在运行的函数入口放一个 ftrace trampoline,跳转到新函数;然后逐个检查所有进程的内核栈,确保它们不在旧函数中;全部安全撤离后旧函数不再被调用。

实现原理

// kernel/livepatch/core.c + kernel/livepatch/patch.c
// 基于 ftrace:

// 1. 注册 livepatch:
klp_enable_patch(patch)klp_patch_object(patch->objs)klp_patch_func(func)  // 对每个要替换的函数
ftrace_set_filter_ip(func->old_func, ...)  // 设 ftrace hook
        → 函数入口被替换为 ftrace trampoline → livepatch handler → 新函数

// 2. 一致性检查 (安全迁移):
klp_try_complete_transition()遍历所有进程 (for_each_process_thread)klp_check_stack(task, func)  // 检查内核栈
      → 如果 task 在 old_func 中 → 不能迁移 → 等待或强制
  → 全部离开 → old_func 不再是任何进程的"当前执行点"

// 3. 完成: patch->state = KLP_PATCHED

一致性模型

// 三种模型:
//   KLP_TRANSITION_IDLE:   等所有进程自愿离开 (等待点: syscall 边界)
//   KLP_TRANSITION_FORCE:  发信号强制离开 (如果可能)
//   KLP_TRANSITION_IMMEDIATE: 立即打补丁 (不等待, 但如果进程在旧函数中→可能 crash)

// 实际大多使用 IDLE (安全) 或信号辅助 (klp_send_signals)

限制

- 不能修改函数签名 (参数类型/数量不变)
- 不能修改数据结构布局 (patch 前后 struct 大小不能变)
- 不能打有状态的补丁 (如果旧代码持有锁 → 补丁后可能死锁)
- livepatch 模块不能卸载 (永久生效)

适合: CVE 修复, 单个函数的防御性检查
不适合: 大规模重构, 跨版本迁移

实时内核 (PREEMPT_RT)

合并状态 (6.x): PREEMPT_RT 正在被合并到主线
  过去: out-of-tree patchset (rt.wiki.kernel.org)
  现在: 逐个 PR 往主线合入 (6.12 接近完成)

与 livepatch 的关系:
  RT 中关抢占改为 rt_mutex → livepatch 的栈检查逻辑需要适配
  → 已处理 (6.5+)

参考

  • 源码⁠: kernel/livepatch/, samples/livepatch/, arch/x86/kernel/livepatch.c
  • 内核文档⁠: Documentation/livepatch/
  • LWN: "Live patching in the Linux kernel"

关键词: livepatch, klp, ftrace, stack check, atomic replace, PREEMPT_RT, CVE fix