LoginSignup
3
3

More than 3 years have passed since last update.

kpatchの使い方

Last updated at Posted at 2019-05-05

1 kpatchとは?

ライブパッチ機能です。
カーネルを再起動することなく、パッチを適用することができます。

2 検証環境

VMware Workstation 14 Player上の仮想マシンを使いました。
仮想マシンは、「最小限のインストール」->「開発ツール」を選択して作成しました。

2.1 カーネル版数

版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

[root@server ~]# uname -r
3.10.0-957.el7.x86_64

2.2 ディスク容量

15Gの空き領域が必要です。
空き領域は、~/.kpatchに作成するキャッシュで使用するようです。

3 コマンドのインストール

kpatch-buildとkpatchをインストールします。
・kpatch-build:パッチからモジュールを作成するコマンド。
・kpatch:モジュールのロード/アンロードをするコマンド。
     モジュールをロードすることで、パッチがカーネルに適用されます。

3.1 パッケージのインストール

kpatch: dynamic kernel patchingのCentOS 7に記載されている手順にしたがって実行しました。

kpatchコマンドに必要なパッケージのインストール
[root@server ~]# UNAME=$(uname -r)
[root@server ~]# yum -y install gcc kernel-devel-${UNAME%.*} elfutils elfutils-devel
kpatch-buildコマンドに必要なパッケージのインストール
[root@server ~]# yum -y install pesign yum-utils zlib-devel binutils-devel newt-devel python-devel perl-ExtUtils-Embed audit-libs audit-libs-devel numactl-devel pciutils-devel bison
[root@server ~]# yum-config-manager --enable debug

[root@server ~]# yum-builddep kernel-${UNAME%.*}
[root@server ~]# debuginfo-install kernel-${UNAME%.*}

[root@server ~]# yum -y install epel-release
[root@server ~]# yum -y install ccache
[root@server ~]# ccache --max-size=5G
Set cache size limit to 5.0 GB

[root@server ~]# yum -y install patchutils

3.2 コマンドのインストール

リポジトリのクローン
[root@server ~]# git clone https://github.com/dynup/kpatch.git
Cloning into 'kpatch'...
remote: Enumerating objects: 41, done.
remote: Counting objects: 100% (41/41), done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 6609 (delta 13), reused 28 (delta 10), pack-reused 6568
Receiving objects: 100% (6609/6609), 1.84 MiB | 267.00 KiB/s, done.
Resolving deltas: 100% (3932/3932), done.
コマンドのコンパイル
[root@server ~]# cd kpatch/
[root@server kpatch]# make

make installを実行すると、kpatchコマンドとkpatch-buildコマンドがインストールされます。

コマンドのインストール
[root@server kpatch]# make install
[root@server kpatch]# kpatch version
0.6.3

4 パッチの作成

kernel-debuginfoパッケージをインストールすると、
下記ディレクトリにカーネルソースが展開されます。

ディレクトリの確認
[root@server kernel-3.10.0-957.el7]# pwd
/usr/src/debug/kernel-3.10.0-957.el7

[root@server kernel-3.10.0-957.el7]# ls -F
linux-3.10.0-957.el7.x86_64/

ディレクトリをコピーします。

ディレクトリのコピー
[root@server kernel-3.10.0-957.el7]# cp -pr linux-3.10.0-957.el7.x86_64 linux-3.10.0-957.el7.x86_64.new

コピー先ディレクトリ(newが付いたディレクトリ)のソースに対して修正を実施します。

ディレクトリの確認
[root@server kernel-3.10.0-957.el7]# ls -F
linux-3.10.0-957.el7.x86_64/  linux-3.10.0-957.el7.x86_64.new/

net/ipv4/ping.cのping_init_sock関数にprintkを1行(下記★)追加します。

ping_init_sockの抜粋
static int ping_init_sock(struct sock *sk)
{
        struct net *net = sock_net(sk);
        kgid_t group = current_egid();
        struct group_info *group_info;
        int i, j, count;
        kgid_t low, high;
        int ret = 0;

     ★ printk(KERN_ERR "TEST:ping_init_sock(1)");

        inet_get_ping_group_range_net(net, &low, &high);
        if (gid_lte(low, group) && gid_lte(group, high))
                return 0;
-以下、略-

修正前後のping.cの差分を抽出します。抽出した差分がパッチとなります。
パッチは、test.patchというファイル名で保存します。

パッチの作成
[root@server kernel-3.10.0-957.el7]# diff -Nur linux-3.10.0-957.el7.x86_64 linux-3.10.0-957.el7.x86_64.new > /root/test.patch

作成したパッチの中身を確認します。

パッチの確認
[root@server kernel-3.10.0-957.el7]# cat /root/test.patch
diff -Nur linux-3.10.0-957.el7.x86_64/net/ipv4/ping.c linux-3.10.0-957.el7.x86_64.new/net/ipv4/ping.c
--- linux-3.10.0-957.el7.x86_64/net/ipv4/ping.c 2018-10-05 05:18:19.000000000 +0900
+++ linux-3.10.0-957.el7.x86_64.new/net/ipv4/ping.c     2019-05-05 10:32:32.825126680 +0900
@@ -211,6 +211,8 @@
        kgid_t low, high;
        int ret = 0;

+        printk(KERN_ERR "TEST:ping_init_sock(1)");
+
        inet_get_ping_group_range_net(net, &low, &high);
        if (gid_lte(low, group) && gid_lte(group, high))
                return 0;

5 モジュールの作成

kpatch-buildコマンドを使って、パッチからモジュールを作成します。
kpatch-buildコマンドの初回実行時は、結構時間(40分くらい)がかかりました。

モジュールの作成
[root@server ~]# ls
anaconda-ks.cfg  kpatch  test.patch

[root@server ~]# kpatch-build -t vmlinux test.patch
Fedora/Red Hat distribution detected
Downloading kernel source for 3.10.0-957.el7.x86_64
Unpacking kernel source
Testing patch file(s)
Reading special section data
Building original source
Building patched source
Extracting new and modified ELF sections
ping.o: changed function: ping_init_sock
Patched objects: vmlinux
Building patch module: livepatch-test.ko
SUCCESS

モジュール(livepatch-test.ko)が作成されたことがわかります。

モジュールの確認
[root@server ~]# ls -F
anaconda-ks.cfg  kpatch/  livepatch-test.ko  test.patch

6 パッチの適用

モジュールをロードして、動作中のカーネルに対してパッチを適用してみます。
まだ何もモジュールがロードされていないことがわかります。

モジュールの確認
[root@server ~]# kpatch list
Loaded patch modules:

Installed patch modules:

モジュール(livepatch_test)をロードします。

モジュールのロード
[root@server ~]# kpatch load livepatch-test.ko
loading patch module: livepatch-test.ko
waiting (up to 15 seconds) for patch transition to complete...
transition complete (2 seconds)

モジュール(livepatch_test)がロードできたことがわかります。

モジュールの確認
[root@server ~]# kpatch list
Loaded patch modules:
livepatch_test [enabled]

Installed patch modules:

7 実験結果

適用したパッチが動作するかどうか確認してみます。
printkの出力結果をジャーナルで確認するため、journalctlコマンドを実行します。
journalctlコマンドの使い方は、ここ(journalctl コマンドの使い方)を参照してください。

ジャーナルの確認
[root@server ~]# journalctl -f

もう1つターミナルを開きます。
デフォルトGWに対してpingを1回実行してみます。
pingコマンドの使い方は、ここ(pingコマンドの使い方)を参照してください。

pingの実行
[root@server ~]# ping -c 1 192.168.3.1

私の環境では、pingを1回実行しても、ジャーナルが出力されませんでした。

ジャーナルの確認
[root@server ~]# journalctl -f
 5月 05 11:25:37 server kernel: TEST:ping_init_sock(1)
 5月 05 11:25:37 server kernel: TEST:ping_init_sock(1)

1回実行した後、さらに、もう1回実行すると、2回分のジャーナルが出力されました。
何故そのような挙動になるのか理解できませんが、とりあえず、
動作中のカーネルに対して、再起動なしでパッチが適用できることは確認できました。

systemtapで確認すると、pingを1回実行すると、ping_init_sock関数が1回
呼ばれていました。

8 その他

8.1 モジュールのアンロード方法

モジュールのアンロードは、以下のようにコマンドを実行します。

モジュールの確認
[root@server ~]# kpatch list
Loaded patch modules:
livepatch_test [enabled]

Installed patch modules:
モジュールのアンロード
[root@server ~]# kpatch unload livepatch_test
disabling patch module: livepatch_test
waiting (up to 15 seconds) for patch transition to complete...
transition complete (1 seconds)
unloading patch module: livepatch_test
モジュールの確認
[root@server ~]# kpatch list
Loaded patch modules:

Installed patch modules:

8.2 古いCentOSで実行した場合の挙動

今現在(2019/5/6)、CentOS7の最新はCentOS7.6です。
CentOS7.4でkpatchを試したのですが、下記エラーがでて、モジュールの作成ができませんでした。

[root@server ~]# kpatch-build --skip-gcc-check -t vmlinux test.patch
WARNING: Skipping gcc version matching check (not recommended)
Fedora/Red Hat distribution detected
Downloading kernel source for 3.10.0-693.el7.x86_64
ERROR: kpatch build failed. Check /root/.kpatch/build.log for more details.

上記エラーは、下記★行のdie関数で出力しています。
シェルスクリプトの一部を以下に引用しました。

[root@server ~]# less /usr/local/bin/kpatch-build

                if [[ -z "$SRCRPM" ]]; then
                        if [[ "$DISTRO" = fedora ]]; then
                                wget -P "$TEMPDIR" "http://kojipkgs.fedoraproject.org/packages/kernel/$KVER/$KREL/src/kernel-$KVER-$KREL.src.rpm" 2>&1 | logger || die
                        else
                                command -v yumdownloader &>/dev/null || die "yumdownloader (yum-utils or dnf-utils) not installed"
                            ★  yumdownloader --source --destdir "$TEMPDIR" "kernel$ALT-$KVER-$KREL" 2>&1 | logger || die
                        fi
                        SRCRPM="$TEMPDIR/kernel$ALT-$KVER-$KREL.src.rpm"
                fi

以下は、★印の行を抜き出したものです。

yumdownloader --source --destdir "$TEMPDIR" "kernel$ALT-$KVER-$KREL" 2>&1 | logger || die

sourceは、バイナリRPMではなく、ソースRPMをダウンロードするオプションです。
destdirは、ダウンロードするRPMの保存場所(/root/.kpatch/tmp)を指定するオプションです。
上記1行は、以下のように展開されます。

yumdownloader --source --destdir /root/.kpatch/tmp kernel-3.10.0-693.el7 2>&1 | logger || die

yumdownloaderを単独で実行すると、以下のように「ソースRPMがない」という結果になりました。

[root@server ~]# yumdownloader --source --destdir /root/.kpatch/tmp kernel-3.10.0-693.el7
読み込んだプラグイン:fastestmirror, langpacks
Enabling epel-source repository
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: ftp.riken.jp
 * epel-source: ftp.riken.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
No source RPM found for kernel-3.10.0-693.el7.x86_64
Nothing to download

一方、最新(CentOS7.6)のソースRPMをダウンロードしてみると、成功しました。
つまり、yumdownloaderが参照するリポジトリには、最新のRPMしか存在しない、
ということのようです。

[root@server ~]# yumdownloader --source --destdir /tmp kernel-3.10.0-957.el7
読み込んだプラグイン:fastestmirror, langpacks
Enabling epel-source repository
Loading mirror speeds from cached hostfile
 * base: ftp.riken.jp
 * epel: ftp.riken.jp
 * epel-source: ftp.riken.jp
 * extras: ftp.riken.jp
 * updates: ftp.riken.jp
No Presto metadata available for base-source
kernel-3.10.0-957.el7.src.rpm                                                                                                         |  96 MB  00:06:05

[root@server ~]# ls -l /tmp/kernel-3.10.0-957.el7.src.rpm
-rw-r--r--. 1 root root 101032927 11月 13 00:31 /tmp/kernel-3.10.0-957.el7.src.rpm

そこで、リポジトリの定義を参照してみました。
ソースRPMのリポジトリは、/etc/yum.repos.d/CentOS-Sources.repoに定義されています。
baseurlの部分だけを抜き出して、$releaseverを7に置き換えると、以下のようになります。
ブラウザでbaseurlを参照してみたのですが、CentOS7.4のソースRPMはありませんでした。

[base-source]
baseurl=http://vault.centos.org/centos/7/os/Source/
[updates-source]
baseurl=http://vault.centos.org/centos/7/updates/Source/
[extras-source]
baseurl=http://vault.centos.org/centos/7/extras/Source/
[centosplus-source]
baseurl=http://vault.centos.org/centos/7/centosplus/Source/

結論として、下記エラーがでて、モジュールの作成ができないのは、
kpatch-buildのバグではないかと思います。

ERROR: kpatch build failed. Check /root/.kpatch/build.log for more details.

kpatch-buildを実行しているCentOSの版数と同じ版数のソースRPMを
ダウンロードできるようにしなくてはいけないと思います。
$releaseverの部分を7ではなく、7.4.1708に置き換えることができれば
上手くいくと思うのだけど。う~ん、どうしよう。。。

Z 参考情報

Red Hat Enterprise Linux 7.2 以降における kpatch のガイド
Linux live kernel patching with kpatch on CentOS 7
kpatch - live kernel patching

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3