この記事はLinux Advent Calendar 2016の1日目の記事です。 テストする時にstubって便利ですよね。最近はnodejsで仕事のコードを書いているのでsinon便利だなとか思ってます。じゃあ、Linuxカーネルでもstub作れたら便利だよねという思うわけです。そうすると、我々にはlivepatchという機能があります。 というわけで、Linuxカーネルのlivepatchを使ってstubを作れるようにしてみようと思ったのが今回の記事のネタです。
今回のコードはLinux 4.9.0-rc4で動かしています。 stub機能の前提としてlivepacthを使うのでstubのコードはlivepatch方式でカーネルモジュールとして作成します。どんな関数をどのようにstubするかはカーネルモジュールで決めるので、ここは使う人の自由となります。カーネル側はstubをするための機能を実装する形です。
差分はこのようになってます。bc33b0〜はLinux 4.9-rc4をリリースした時のコミットです。
masami@kerntest:~/linux-kernel (kstub=)$ git diff --stat bc33b0ca11e3df467777a4fa7639ba488c9d4911 arch/s390/include/asm/livepatch.h | 5 ++++ arch/x86/include/asm/livepatch.h | 5 ++++ include/linux/kstub.h | 59 +++++++++++++++++++++++++++++++++++++++ include/linux/livepatch.h | 1 + init/main.c | 3 ++ kernel/livepatch/core.c | 20 +++++++++++++- lib/Kconfig.debug | 7 +++++ lib/Makefile | 1 + lib/kstub.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ samples/kstub/Makefile | 17 ++++++++++++ samples/kstub/kstub_test.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 452 insertions(+), 1 deletion(-)
この差分の内訳としては、stub機能を実現するためのコードがだいたいlib/にあり、stubする・しないを判定する処理を付けたかったのでlivepatch側にそれの対応を入れています。あとsample/にサンプルコードを置いてます。
stubする・しないの判定は以前に書いたlinux kernelのlivepatchにライブパッチを適用する・しないの判断機能を入れて遊ぶ - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモからの流用です。
sampleディレクトリのコードをビルドして、例えばmoreコマンドの時にstubを適用するとこのようになります。
masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ sudo insmod ./kstub_test.ko target_name=more masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ cat /proc/version Linux version 4.9.0-rc4-kstub+ (masami@kerntest) (gcc version 6.2.1 20160830 (GCC) ) #144 SMP Sat Nov 19 12:42:38 JST 2016 masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ more /proc/version linux version masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ cat /proc/cmdline BOOT_IMAGE=/boot/vmlinuz-4.9.0-rc4-kstub+ root=UUID=538fe610-066c-4939-9d33-18a80f7a28a0 rw quiet console=tty0 console=ttyS0,115200n8 crashkernel=128M masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$ more /proc/cmdline hello, world masami@kerntest:~/linux-kernel/samples/kstub (kstub=)$
サンプルコードはこのようなコードです。stubの処理はlivepatchをラップしているのでlivepatchを直接使うよりはコードが短くなってると思います。
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h> #include <linux/kernel.h> #include <linux/kstub.h> #include <linux/seq_file.h> #include <linux/string.h> #include <linux/sched.h> MODULE_DESCRIPTION("kstub test module"); MODULE_AUTHOR("masami256"); MODULE_LICENSE("GPL"); static char *target_name = NULL; module_param(target_name, charp, S_IRUGO); MODULE_PARM_DESC(target_name, "Target process name"); static struct kstub *kstub; static int kstub_cmdline_proc_show(struct seq_file *m, void *v) { seq_printf(m, "hello, world\n"); return 0; } static int kstub_version_proc_show(struct seq_file *m, void *v) { seq_printf(m, "linux version\n"); return 0; } static bool kstub_need_patch_apply(void) { return !strcmp(current->comm, target_name); } static struct kstub_stub_data stubs[] = { { .func_name = "cmdline_proc_show", .stub_func = kstub_cmdline_proc_show, }, { .func_name = "version_proc_show", .stub_func = kstub_version_proc_show, }, {} }; static int kstub_test_init(void) { if (!target_name) { pr_info("target name is required\n"); return -EINVAL; } kstub = KSTUB_CREATE(); if (IS_ERR(kstub)) goto out; return kstub->ops.setup_stub(kstub, stubs, kstub_need_patch_apply); out: return PTR_ERR(kstub); } static void kstub_test_cleanup(void) { pr_info("%s\n", __func__); } module_init(kstub_test_init); module_exit(kstub_test_cleanup); MODULE_INFO(livepatch, "Y");
stub機能でstub(livepatchでパッチしてる関数)はdebugfsで見れるようにしています。start_kernel()で/sys/kernel/debugにkstubディレクトリを作成し、stub機能でstubをセットアップする時にカーネルモジュール名のディレクトリを作ってstubsというファイルでstubしている関数名を読めるようにしています。
[root@kerntest kstub]# cat /sys/kernel/debug/kstub/kstub_test/stubs cmdline_proc_show version_proc_show [root@kerntest kstub]#
コードはgithubにあります。
差分はこちらからも見れます。
Comparing torvalds:master...masami256:kstub · torvalds/linux · GitHub
TODOとしてはエラー処理が適当を修正したいとか、stubをenable/disable切り替えたいとかありあす。
現段階での最新になる4.9.0-rc7でも動きます。
masami@kerntest:~/linux-kernel/samples/kstub (kstub>)$ cat /proc/version Linux version 4.9.0-rc7-kstub+ (masami@kerntest) (gcc version 6.2.1 20160830 (GCC) ) #145 SMP Thu Dec 1 00:16:51 JST 2016 masami@kerntest:~/linux-kernel/samples/kstub (kstub>)$ more /proc/version linux version
Happy Hacking 🍣🍣🍣
これ一冊で完全理解 Linuxカーネル超入門(日経BP Next ICT選書)
- 作者: 日経Linux
- 出版社/メーカー: 日経BP社
- 発売日: 2014/10/16
- メディア: Kindle版
- この商品を含むブログを見る