1、基本原理以及使用教程,参考:
https://github.com/dynup/kpatch/blob/meastr/README.md
https://github.com/dynup/kpatch/blob/master/doc/patch-author-guide.md
2、测试环境:
系统:CentOS 7
内核版本:3.10.0-957.5.1.el7.x86_64
工具:gcc 4.8.5,ccache(官方建议安装,可以明显提高build速度)
测试用例:
kpatch/test/integration/centos-7目录
kpatch/test/testmod目录
3、给内核(vmlinux)打热补丁:
对应命令:kpatch-build -t vmlinux xxx.patch(会根据当前运行的内核版本找到源码目录)
或者:kpatch-build -s src_dir -t vmlinux xxx.patch (指定内核源码目录)
其中xxx.patch对应生成的patch文件,利用diff工具生成,参照下面命令:
diff -urNap src.orig src > diff.pacth //比对目录
diff -uNap test.c.org test.c > diff.patch //对比单个文件
用例:kpatch/test/integration/centos-7/data-new.patch
会在当前目录下生成一个livepatch-data-new.ko,目前3.10内核已经原生支持内核热补丁,叫做livepatch,生成的补丁会以livepach作为前缀。如果内核不支持,则生成kpatch为前缀的模块,并且加载热补丁模块前,需要加载kpatch.ko模块。
该patch定义了一个全局变量,值为5,并在/proc/meminfo中加了一行打印,然后输出这个全局变量。加载模块并执行:
insmod livepatch-data-new.ko //或 kpatch load livepacth-data-new.ko
cat /proc/meminfo
可以看出,多了一行 kpatch: 5 的打印,热补丁应用成功。
如果需要临时关闭或打开补丁功能,可以执行:
echo 0 > /sys/kernel/livepatch/livepatch_data_new/enabled //关闭
echo 1 > /sys/kernel/livepatch/livepatch_data_new/enabled //打开
卸载模块:
出于安全考虑,一般卸载并不是立马生效,可以看出会等待一个时间,保证没有别的函数调用。
此处注意:不要用rmmod方式卸载,会报错:
4、给内核模块打热补丁
用例:kpatch/test/integration/centos-7/module-shadow.patch
该用例是给kvm模块打了个patch,构建方式和前面有区别:
命令:
kpatch-build -t modules module-shadow.patch
此处-t后面改为modules,其实-t后面就是你make后面的target,比如你要编译内核模块,是用make modules,所以-t后面用modules,如该你此处用vmlinux,则会提示no changed objects found。 该patch其实就是给kvm-intel模块打了个热补丁。
5、给私有模块打热补丁
用例:kpatch/test/testmod
该用例也是给module打补丁,但是区别是该patche是给一个第三方的module打补丁。官方解释为out-of-tree module,我个人理解就是非linux内核主线维护的,比如你自己写的驱动,或者某些厂家提供的定制驱动等。
运行该用例前需要改造一下代码,用例代码两年没更新了,没有用kpatch-build构建,而是直接用了kpath-build目录下的create-diff-object二进制程序,你跑doit.sh脚本会自动构建,但是会报错,提示参数不对,应该是用例代码没更新,跟不上create-diff-object的更新。
此处我们用kpatch构建,改造一下Makefile:
testmod.ko: testmod_drv.c
# patch < patch
# KCFLAGS="-ffunction-sections -fdata-sections" $(MAKE) -C $(BUILD) M=$(PWD) testmod.ko
# strip --keep-file-symbols -d testmod_drv.o
# cp testmod_drv.o testmod_drv.o.patched
# patch -R < patch
# KCFLAGS="-ffunction-sections -fdata-sections" $(MAKE) -C $(BUILD) M=$(PWD) testmod.ko
# strip --keep-file-symbols -d testmod_drv.o
# cp testmod_drv.o testmod_drv.o.orig
# $(MAKE) -C $(BUILD) M=$(PWD) clean
$(MAKE) -C $(BUILD) M=$(PWD) testmod.ko
all: testmod.ko
clean:
$(MAKE) -C $(BUILD) M=$(PWD) clean
# rm *.orig *.patched
kbuild rules:
obj-m := testmod.o
testmod-y := testmod_drv.o
注释掉一些行。注释的行其实在kpatch-build命令中,已经帮我们做了。执行make all,会生成testmod.ko模块,加载模块看看:
patch内容:
--- testmod_drv.c.orig 2014-06-02 16:49:49.428509600 -0500
+++ testmod_drv.c 2014-06-02 16:49:56.973656791 -0500
@@ -11,7 +11,7 @@
static ssize_t value_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- return sprintf(buf, "%d\n", value);
+ return sprintf(buf, "%d\n", value+1);
}
static struct kobj_attribute testmod_value_attr = __ATTR_RO(value);
可以看出只是简单做了+1操作,所以预期执行结果应该是那个value变成3。
进入testmod目录执行:
kpatch-build -s ./ -t all -e ./testmod.ko patch //-e 参数指明你编译出来的原始的module
执行后报错:
ERROR: /root/kpatch/test/testmod/patch file failed to apply. Check
/root/.kpatch/build.log for more details.
原因是因为patch出了问题,改造一下patch文件:
先复制一份:cp pacth diff.patch,修改如下:
--- patch 2019-02-26 10:54:16.428000000 +0800
+++ diff.patch 2019-03-13 11:28:52.824000000 +0800
@@ -1,5 +1,5 @@
---- testmod_drv.c.orig 2014-06-02 16:49:49.428509600 -0500
-+++ testmod_drv.c 2014-06-02 16:49:56.973656791 -0500
+--- a/testmod_drv.c 2014-06-02 16:49:49.428509600 -0500
++++ b/testmod_drv.c 2014-06-02 16:49:56.973656791 -0500
@@ -11,7 +11,7 @@
static ssize_t value_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
再次执行,仍然会报错,这次是由于kpatch-build的bug导致,参看:
https://github.com/dynup/kpatch/issues/941
解决方法:
https://github.com/joe-lawrence/kpatch/commit/afcfbfa9ba6c77abbbd1caef3323632df52b982d
修改后再次执行构建:
加载livepacth-diff.ko后:
可以看出,确实和预期一样,value变为3,关闭后,又恢复原来值。内核热补丁生效。