热补丁原理
1. 代码替换机制:Ftrace 与跳转指令
热补丁的核心技术基于 Ftrace(Function Tracer) 框架:
- 跳转指令替换:利用 x86 架构下
call 指令(5 字节)或 ARM64 的 b 指令,在函数入口处插入跳转到新函数的指令 - 运行时原子写入:通过
text_poke() 等接口在运行中安全地修改内核代码段(.text),通常需要内存页写权限临时开启 - 旧函数保留:原始函数体保留,仅入口被"劫持",保证正在执行的旧函数实例能安全返回
2. 一致性模型:Klp 与 Kgraft
Linux 社区主要采用两种一致性保证策略:KLP (Kernel Live Patching) - 主线采用:
- 停止点(Consistency Model):利用内核已有的
klp_update_patch_state() 机制,通过任务状态标志位跟踪 - 原子切换:当所有 CPU 都经过安全点(如系统调用返回、中断返回)时,新代码路径对所有后续调用生效
- 栈回溯检查:确保没有任务正在执行被补丁函数的旧版本(通过
walk_stack() 检查)Kgraft - SUSE 方案(历史): - 采用类似 RCU 的延迟释放机制,旧代码持续运行直到所有引用退出
3. 数据结构:Livepatch 模块
// 核心结构体structklp_func {void *old_func; // 指向原函数void *new_func; // 指向补丁函数char *old_name; // 符号名(用于查找)structklp_object *obj;};structklp_patch {structklp_object *objs;structlist_headlist;bool enabled;// ...};
4. 加载与激活流程
- 模块加载:通过
insmod patch.ko 加载包含新函数的 ELF 模块 - 符号解析:
klp_resolve_symbols() 解析旧函数地址(通过 kallsyms) - 注册补丁:调用
klp_enable_patch() 注册到 livepatch 子系统 - 一致性等待:遍历所有任务,检查栈中是否包含被替换函数
- 原子激活:所有 CPU 同步后,通过
arch_klp_patch_func() 写入跳转指令
5. 限制与边界
- 非语义变更:只能修改函数实现,不能改变数据结构布局(结构体字段增减)
- 无静态变量变更:不能修改全局/静态变量语义,因为旧模块仍在引用
- 调用约定保持:新函数必须保持与原函数相同的 ABI(参数、返回值、调用约定)
- 安全点依赖:需要内核配置
CONFIG_LIVEPATCH 和 CONFIG_DYNAMIC_FTRACE
热补丁制作工具
kpatch-build kpatch-build 是一款热补丁制作工具,可在不重启系统和中断业务的情况下对操作系统内核进行CVE和Bug修复。 支持架构: X86,ARM64 上游社区:https://github.com/dynup/kpatch![[Pasted image 20260508174701.png]]
准备工作
以Redhat系列linux为例
yum install -y make gcc patch bison flex openssl-devel elfutils elfutils-devel dwarves bc perl
- 内核信息准备 下载kernel-debuginfo,kernel-devel,kernel source
- 安装kpatch-build 可以选择直接按照官方编译的rpm包,或自己编译
- rpm安装yum install kpatch-build
wget https://gitee.com/anolis/kpatch-build/repository/archive/master.zip
解压v0.9.9.tar.gz
调用patch.sh脚本,把仓库中的patch打上源码
./patch.sh
进入kpatch-0.9.9目录下 如果你是需要使用kpatch core module加载(即kpatch.ko),设置BUILDMOD=yes
make BUILDMOD=yes && make BUILDMOD=yes install
如果不需要使用kpatch.ko,使用livepatch
make && make install
kpatch-build使用
kpatch-build -n kpatch-test -s /root/hotfix/linux-4.19.91-26.4.an7 -c /root/hotfix/.config -v /root/hotfix/vmlinux -o /root/hotfix/output/ -dddddd -R /root/hotfix/test-livepatch.patch
其中:
-n 补丁名称-s:指向源代码目录-c: config文件-v: vmlinux文件-o: 产物输出目录test-livepatch.patch:补丁文件-d: 输出debug信息
制作oot hotfix
制作oot hotfix的过程与制作kernel hotfix的过程一致,只是输出参数区别
kpatch-build -s {path_to_kernel_source} -c {path_to_kernel_config} -v {path_to_kernel_vmlinux} --oot-module-src {path_to_oot_module_source} --oot-module {path_to_oot_module_ko} {path_to_patch_file}
使用说明
热补丁中自带kpatch管理工具 加载热补丁:
kpatch load kpatch-test.ko
卸载热补丁:
kpatch unload kpatch-test.ko
热补丁列表:
kpatch list
示例
linux低版本bond驱动新增双发arp功能
准备
--- bonding/bond_main.c 2026-05-01 01:07:34.956327587 -0400+++ bonding/bond_main.c 2026-05-01 01:11:01.920233936 -0400@@ -4614,6 +4614,8 @@ return slave; }+static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,+ struct net_device *bond_dev); /* Use this Xmit function for 3AD as well as XOR modes. The current * usable slave array is formed in the control path. The xmit function * just calculates hash and sends the packet out.@@ -4625,6 +4627,10 @@ struct bond_up_slave *slaves; struct slave *slave;+ if ( skb->protocol == htons(ETH_P_ARP) ) {+ return bond_xmit_broadcast(skb, dev);+ }+ slaves = rcu_dereference(bond->usable_slaves); slave = bond_xmit_3ad_xor_slave_get(bond, skb, slaves); if (likely(slave))
path_to_kernel_source=/lib/modules/4.18.0-372.9.1.el8.x86_64/buildpath_to_kernel_config=/boot/config-4.18.0-372.9.1.el8.x86_64path_to_kernel_vmlinux=/usr/lib/debug/lib/modules/4.18.0-372.9.1.el8.x86_64/vmlinuxpath_to_oot_module_source=/root/bondingorigpath_to_oot_module_ko=/root/bonding.kopath_to_patch_file=/root/arp.patchkpatch-build -s ${path_to_kernel_source} -c ${path_to_kernel_config} -v ${path_to_kernel_vmlinux} --oot-module-src ${path_to_oot_module_source} --oot-module ${path_to_oot_module_ko} ${path_to_patch_file} -t ''
sh build.sh
kpatch load livepatch-arp.ko
注意
热补丁加载时,需要确保所有CPU都完成函数替换,所以如果有其他进程长时间占用CPU,可能导致加载热补丁失败,需要其他进程释放CPU后才能成功。