内核热补丁调研


  • BCLinux Developers

    一、介绍

    内核热补丁是一种无需重启系统,动态为内核打补丁的技术。系统管理员基于该技术,可以在不重启系统的情况下,为内核应用类似安全漏洞等重要的BUG修复,可以在最大程度上减少系统宕机时间,增加系统的可用性。

    最早的热补丁技术是Ksplice,在2011年被Oracle收购后,Ksplice也随之变成闭源。随后Suse和Redhat相继推出了类似的项目,分别是Kgraft和Kpatch,下面分别对这三个项目做下介绍:

    Ksplice

    Ksplice是热补丁技术的创始者,并于2008年建立了与项目同名的公司。Ksplice公司免费提供软件,但是技术支持是需要收费的。Oracle 在 2011 年收购了Ksplice公司,这项功能被合入Oracle自己的Linux发行版:Oracle Linux中,并只对Oracle提供技术更新。

    Kpatch

    2014年2月,红帽基于GPLv2协议发布了Kpatch项目,在RHEL 7.0中,这项技术作为“技术预览”首次亮相,在RHEL 7.2之后正式支持。

    Kgraft

    2014年3月,Suse基于GPLv2/v3协议发布了Kgraft项目,并将该技术集成到Suse Linux Enterprise Server 12版本中。

    本文重点分析红帽的kpatch热补丁技术。

    二、原理分析

    Kpatch基于ftrace实现内核函数的替换,类似于ftrace的动态探测点。利用mcount机制,在内核编译时在每个函数入口保留数个字节,然后在打补丁时将“被替换函数”入口保留的字节替换为跳转指令,跳转到Kpatch的相关流程中,最终进入“新函数”的执行流程,实现函数级别的执行流程在线替换。具体而言如下图:
    0_1498312123458_Linux_kernel_live_patching_kpatch.svg.png

    1. 将新函数加载进内存
    2. 链接新函数到内核,并允许访问unexported内核符号
    3. 活动安全检查,防止新旧函数同时运行(stop_machine()以及函数调用栈检查)
    4. 使用ftrace替换旧函数

    Kpatch这个项目,主要包含以下4个组件:

    • kpatch-build
      用于将 source diff patch转换成hot patch module的工具,分别编译不带补丁和带补丁的内核版本,并比较binaries(即vmlinux),生成包含hot patch的模块。
    • hot patch module
      提供hot patch模块注册新函数的接口的内核模块(.ko文件),该模块利用ftrace子系统hook到原始函数的mcount回调函数,所以对原始函数的调用将被redirect到替代函数;
    • kpatch core module
      为hot patch注册新的函数以用于替换提供接口的内核模块;
    • kpatch utility
      允许用户管理hotpatch模块的命令行工具;

    其中kpatch-build的编译原理如下图:
    0_1498311764572_14981244722259.jpg

    1. 编译内核(with/without patch)
    2. 比较编译后的内核二进制文件
    3. 检查哪个函数被修改
    4. 提取该函数的二进制代码
    5. 生成内核模块

    三、使用方法

    BCLinux 7.2版本已经支持Kpatch,本文以内核版本3.10.0-327.36.3.el7.x86_64为例,介绍Kpatch的使用方法。

    1. 安装编译kpatch依赖的软件包

    $ UNAME=$(uname -r)
    $ sudo yum install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel
    

    2. 安装编译kpatch-builder依赖的软件包

    $ sudo yum install rpmdevtools pesign yum-utils zlib-devel \
    >   binutils-devel newt-devel python-devel perl-ExtUtils-Embed \
    >   audit-libs audit-libs-devel numactl-devel pciutils-devel bison
    

    增加内核debug软件仓库:

    $ sudo cat /etc/yum.repos.d/debug.repo
    [debug] 
    name=BCLinux-$releasever - Debug
    #baseurl=http://mirrors.bclinux.org/bclinux/el7.2/debuginfo/x86_64/
    baseurl=http://debuginfo.centos.org/7/x86_64/
    gpgcheck=0 
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 
    enabled=1
    

    安装编译内核的依赖包和内核debuginfo软件包:

    $ sudo yum-builddep kernel-${UNAME%.*}
    $ sudo debuginfo-install kernel-${UNAME%.*}
    

    安装和设置编译缓存(可选):

    $ sudo yum install ccache
    $ ccache --max-size=5G
    

    3. 编译和安装kpatch

    将kpatch代码从github中clone到本地:

    $ git clone https://github.com/dynup/kpatch.git
    Cloning into 'kpatch'...
    remote: Counting objects: 4848, done.
    remote: Total 4848 (delta 0), reused 0 (delta 0), pack-reused 4848
    Receiving objects: 100% (4848/4848), 1.25 MiB | 75.00 KiB/s, done.
    Resolving deltas: 100% (2780/2780), done.
    

    编译并安装:

    $ cd kpatch/
    $ make
    $ sudo make install 
    

    4. Kpatch补丁生成

    我们使用官方的例子对Kpatch进行测试,修改/proc/meminfo里面的关键字VmallocChunk为全大写:

    --- a/fs/proc/meminfo.c 2015-10-30 04:56:51.000000000 +0800
    +++ b/fs/proc/meminfo.c 2017-06-23 13:06:32.509918931 +0800
    @@ -131,7 +131,7 @@
                    "Committed_AS:   %8lu kB\n"
                    "VmallocTotal:   %8lu kB\n"
                    "VmallocUsed:    %8lu kB\n"
    -               "VmallocChunk:   %8lu kB\n"
    +               "VMALLOCCHUNK:   %8lu kB\n"
     #ifdef CONFIG_MEMORY_FAILURE
                    "HardwareCorrupted: %5lu kB\n"
     #endif
    

    下载内核源码包,利用kpatch-build命令构建生成热补丁ko模块:

    # wget http://mirrors.bclinux.org/bclinux/el7.2/os/Source/SPackages/kernel-3.10.0-327.36.3.el7.src.rpm
    # kpatch-build meminfo-string.patch -r kernel-3.10.0-327.36.3.el7.src.rpm
    Using cache at /root/.kpatch/src
    Testing patch file
    checking file fs/proc/meminfo.c
    Reading special section data
    Building original kernel
    Building patched kernel
    Extracting new and modified ELF sections
    meminfo.o: changed function: meminfo_proc_show
    Patched objects: vmlinux
    Building patch module: kpatch-meminfo-string.ko
    SUCCESS
    

    5. Kpatch补丁安装和测试

    安装kpatch补丁:

    $ sudo kpatch install kpatch-meminfo-string.ko
    installing kpatch-meminfo-string.ko (3.10.0-327.36.3.el7.x86_64)
    

    启动kpatch服务,确认补丁已经加载进内核:

    $ sudo systemctl enable kpatch
    
    $ sudo systemctl start kpatch
    
    $ sudo kpatch list
    Loaded patch modules:
    kpatch_meminfo_string
    
    Installed patch modules:
    kpatch_meminfo_string (3.10.0-327.36.3.el7.x86_64)
    

    验证内核补丁被加载成功,/proc/meminfo中的关键字VmallocChunk为全大写:

    $ cat /proc/meminfo | grep -i VmallocChunk
    VMALLOCCHUNK:   34359698684 kB
    

    6. Kpatch补丁卸载

    $ sudo kpatch unload kpatch_meminfo_string
    disabling patch module: kpatch_meminfo_string
    unloading patch module: kpatch_meminfo_string
    $ sudo  kpatch list
    Loaded patch modules:
    
    Installed patch modules:
    kpatch_meminfo_string(3.10.0-327.36.3.el7.x86_64)
    
    $ sudo kpatch uninstall kpatch_meminfo_string
    uninstalling kpatch_meminfo_string(3.10.0-327.36.3.el7.x86_64)
    
    $ sudo kpatch list
    Loaded patch modules:
    
    Installed patch modules:
    
    

    7. 内核当前有无热补丁判断

    通过dmesg打印判断:

    $  dmesg | grep TAINT_LIVEPATCH
    [39945.410517] kpatch: tainting kernel with TAINT_LIVEPATCH
    

    /proc/sys/kernel/tainted内容,第15位是否为1。

    四、使用限制

    Kpatch虽然是一种很好的解决内核升级的方法,但是在实际使用过程中具有如下限制:

    1. Kpatch不适用于通用内核版本的升级,它适用于在系统不用立即重启情况下进行简单的安全或者漏洞修复。
    1. 不是所有代码改动都能打补丁,如果补丁涉及到内核关键数据结构的改动,使用kpatch-build生成热补丁模块会失败。
    1. 不要在加载补丁的过程中使用SystemTap或者kprobe探针,因为补丁可能会失效,除非移除这些探针。
    1. 不要同时尝试打印ftrace输出,例如cat /sys/kernel/debug/tracing/trace,建议使用trace-cmd工具。
    1. 在使用kpatch的过程中系统不能待机或者睡眠,因为这样可能会导致补丁临时不生效。
    1. 依赖的内核版本:
      gcc >= 4.8 & Linux >= 3.9.

    五、业务场景分析

    前面提到了kpatch不能解决所有的内核升级问题,如果升级补丁较多改动或者涉及到内核数据结构的变化,则无法涵盖。另外,在应用内核补丁时,需要调用stop_machine()函数以确保当前没有线程正在调用老的函数,这会造成一定的系统响应延迟。

    基于以上的分析,可以在如下的业务场景中尝试使用内核热补丁技术:

    1. 内核的改动量不大,而且不涉及到核心数据结构的改变。例如高危的安全漏洞,紧急的BUG修复等等。

    2. 内核BUG,在无法确认补丁是否可以修复的情况下,可以使用热补丁技术验证修复方案的正确性。

    3. 通过热补丁技术,协助解决线上内核BUG。通过热补丁在内核中动态添加调试信息,有助于定位内核BUG。卸载热补丁后,将自动去除添加调试信息,对原来内核无影响。

    商业模式可以借鉴红帽和OL,将其对外封装成一种高级服务。只针对高级客户或者购买了此项服务的客户开放,按照服务次数收费。客户针对某一内核漏洞,发出内核热补丁请求,我方评估此请求是否合理(热补丁是否能够涵盖)。如果合理,则针对此内核漏洞制作内核热补丁,并封装成RPM包交付给客户。

    客户按照操作手册,安装RPM包自动加载其中的内核补丁,从而完成内核热补丁的升级过程。

    六、参考文献


登录后回复
 

与 BC-LINUX 的连接断开,我们正在尝试重连,请耐心等待