2016年7月13日〜15日で行われたLinuxCon Japan 2016のセッションでカーネルハックの一環でカーネルのlivepatchの機能を使って、FreeBSDのバイナリをLinuxで動かすってセッションに参加して、そういう使い方もありだなと思ったのでちょっと試してみました。アリだなと思ったところは条件に応じてライブパッチするしないの判断をできるようにすれば便利だなってとこだったので、そのへんを試してみた感じです。
まず、linux kernelのlivepatch機能を条件判断できるように変更します。
livepatch機能の変更点はpatch対象の関数が呼ばれた時のハンドラ関数のklp_ftrace_handler()です。ここで、条件判断の関数を呼んで、その関数がtrueを返したら新しい関数を呼び、falseの場合はオリジナルの関数を引き続き実行できるようにしています。
- klp_arch_set_pc(regs, (unsigned long)func->new_func); + if (likely(klp_need_patch_apply_func())) + klp_arch_set_pc(regs, (unsigned long)func->new_func); + else + klp_arch_enter_orig_func(regs, ip); +
条件判断の関数はカーネル本体にはデフォルトの関数だけ作っていて、ライブパッチの登録時に変更するようにしました。Linux Kernelのlivepatchはカーネルモジュールとして作るので、条件判断の関数もlivepatch用のカーネルモジュール内で作ります。
上のコードは、以下の3パターンの条件判断のうちどれかを選択できるようにしてます。
例えば、特定のPID名前空間のプロセスを対象にする場合、条件判断式は↓を使います。
static bool need_patch_apply_by_pidns(void) { struct pid_namespace *pidns = task_active_pid_ns(current); return target_inum == pidns->ns.inum; }
この場合、PID名前空間の指定はinode番号を使います。
実際の例でいくと、dockerコンテナ内のプロセスを対象にするとして、動かしているpid:1071のPID名前空間のinodeを調べます。
masami@kerntest:~$ sudo pstree -p systemd(1)─┬─agetty(339) ├─agetty(340) ├─dbus-daemon(251) ├─dhcpcd(335) ├─docker(761)─┬─docker-containe(779)─┬─docker-containe(1027)─┬─bash(1043)───cmdline(1071) │ │ │ ├─{docker-containe}(1028) │ │ │ ├─{docker-containe}(1029) │ │ │ ├─{docker-containe}(1030) │ │ │ ├─{docker-containe}(1031) │ │ │ ├─{docker-containe}(1032)
pidがわかればそこで使っている名前空間のinode番号は/procを見ることでわかります。
masami@kerntest:~/livetest$ sudo ls -la /proc/1071/ns/pid lrwxrwxrwx 1 root root 0 Jul 16 11:33 /proc/1071/ns/pid -> 'pid:[4026532216]'
コンテナで動かしているプロセスはこんな感じのやつです。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> void read_cmdline(void) { int fd; char buf[256] = {0}; fd = open("/proc/cmdline", O_RDONLY); if (fd == -1) { perror("open"); exit(-1); } read(fd, buf, sizeof(buf) - 1); close(fd); printf("%s", buf); } int main(int argc, char **argv) { printf("pid is %d\n", getpid()); while (1) { read_cmdline(); sleep(1); } return 0; }
後はカーネルモジュールをinsmodするだけです。
masami@kerntest:~/livetest$ sudo insmod livetest.ko target_inum=4026532216
そうすると、dockerコンテナ内で動いているプロセスが最初は普通に/proc/cmdlineの初期値を読んでいたのに、livepatchモジュールの読み込みにより出力内容が変わります。
masami@kerntest:~/livetest$ cd && sudo docker run --rm -v `pwd`:/tmp/test -it base/archlinux /bin/bash [root@e1d3894ff868 /]# cd /tmp/test [root@e1d3894ff868 test]# ./cmdline pid is 7 BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M ~~~略~~~ BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M hello, world hello, world hello, world
dockerコンテナを抜けるとpid名前空間が変わるので、/proc/cmdlineを見ると、またカーネルのコマンドラインが見れます。
^C [root@e1d3894ff868 test]# exit exit masami@kerntest:~$ cat /proc/cmdline BOOT_IMAGE=/boot/vmlinuz-4.6.0-ktest+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M
(´-`).oO(元々のlivepatch機能の意図からは外れるんだけど、ちょっとした実験とかには良さげな気もします
- 作者: Sam Newman,佐藤直生,木下哲也
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/02/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る