Linux Advent Calendar 2017 16日目の記事です。
今日はLinuxのライブパッチ機能について書こうと思います。
以前、Linux-4.0のライブパッチ機能を試してみるという記事を書いたことがあります。
当時は「ライブパッチ?面白そう!」と思いながら、書いた記事に便乗してLinux-4.0のライブパッチ機能を試してみる会(1)を開催したりしていました。それ以降ライブパッチまわりの状況を追わないまま時間が過ぎてしまったので、Advent Calendarに投稿する記事も兼ねて、ライブパッチ機能の現状についてちょっと調べてみました。
Linuxのライブパッチ機能
前述のライブパッチの記事は、ちょうどLinux-4.0がリリースされた頃で、新機能の一つとしてライブパッチ機能が含まれていました。現在のLinuxは 4.14.6
が最新版(Stable)なので、今回は linux-4.0
と linux-4.14.6
でライブパッチ周りのコードがどう変化しているか調べてみます。
- https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.14.6.tar.xz
- https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.0.tar.xz
まずはコード量を見てみる
手始めにコード量の推移を見てみます。ライブパッチ機能だけで見ると、 4.14.6
と 4.0
ではコードの増加は900行弱にとどまっているようで、あまり大きな機能追加は入っていないような印象を受けます。
$ find linux-4.14.6/kernel/livepatch -type f | xargs wc -l
974 kernel/livepatch/core.c
7 kernel/livepatch/core.h
19 kernel/livepatch/Kconfig
3 kernel/livepatch/Makefile
276 kernel/livepatch/patch.c
34 kernel/livepatch/patch.h
579 kernel/livepatch/transition.c
15 kernel/livepatch/transition.h
1907 total
$ find linux-4.0/kernel/livepatch -type f | xargs wc -l
1038 kernel/livepatch/core.c
18 kernel/livepatch/Kconfig
3 kernel/livepatch/Makefile
1059 total
サンプルコードを見てみる
次に samples/livepatch
以下にあるサンプルコードの差分を見てみましょう。
$ # 追加された行数。
$ diff -ur linux-4.0/samples/livepatch/livepatch-sample.c linux-4.14.6/samples/livepatch/livepatch-sample.c | grep '^\+' | wc -l
19
$
$ # 削除された行数。
$ diff -ur linux-4.0/samples/livepatch/livepatch-sample.c linux-4.14.6/samples/livepatch/livepatch-sample.c | grep '^\-' | wc -l
2
あまり増減はありませんね。追加された箇所を見てみましょう。
$ diff -ur linux-4.0/samples/livepatch/livepatch-sample.c linux-4.14.6/samples/livepatch/livepatch-sample.c | grep '^\+'
+++ linux-4.14.6/samples/livepatch/livepatch-sample.c 2017-12-14 17:53:15.000000000 +0900
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+ if (!klp_have_reliable_stack() && !patch.immediate) {
+ /*
+ * WARNING: Be very careful when using 'patch.immediate' in
+ * your patches. It's ok to use it for simple patches like
+ * this, but for more complex patches which change function
+ * semantics, locking semantics, or data structures, it may not
+ * be safe. Use of this option will also prevent removal of
+ * the patch.
+ *
+ * See Documentation/livepatch/livepatch.txt for more details.
+ */
+ patch.immediate = true;
+ pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n");
+ }
+
+MODULE_INFO(livepatch, "Y");
なにやら以下のチェック処理が追加されていますね。
+ if (!klp_have_reliable_stack() && !patch.immediate) {
klp_have_reliable_stack()
は単にカーネルコンフィグの CONFIG_STACKTRACE
と CONFIG_HAVE_RELIABLE_STACKTRACE
が有効になっているかどうかを確認しているだけですね。
include/linux/livepatch.h:
161 static inline bool klp_have_reliable_stack(void)
162 {
163 return IS_ENABLED(CONFIG_STACKTRACE) &&
164 IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE);
165 }
変数 patch
はサンプルコード内で以下のような変数宣言になっています。この部分は linux-4.0
と linux-4.14.6
で差異はありません。
samples/livepatch/livepatch-sample.c:
65 static struct klp_patch patch = {
66 .mod = THIS_MODULE,
67 .objs = objs,
68 };
struct klp_patch
を見てみます。構造体メンバ変数として、 immediate
enabled
finish
が追加されています。
$ $ diff -ur linux-4.0/include/linux/livepatch.h linux-4.14.6/include/linux/livepatch.h
...
/**
* struct klp_patch - patch structure for live patching
* @mod: reference to the live patch module
* @objs: object entries for kernel objects to be patched
+ * @immediate: patch all funcs immediately, bypassing safety mechanisms
* @list: list node for global list of registered patches
* @kobj: kobject for sysfs resources
- * @state: tracks patch-level application state
+ * @enabled: the patch is enabled (but operation may be incomplete)
+ * @finish: for waiting till it is safe to remove the patch module
*/
struct klp_patch {
/* external */
struct module *mod;
struct klp_object *objs;
+ bool immediate;
/* internal */
struct list_head list;
struct kobject kobj;
- enum klp_state state;
+ bool enabled;
+ struct completion finish;
};
メンバ変数 immediate
には以下のソースコードコメントが記載されています。どうやらライブパッチ時に行われるセーフティチェックの迂回フラグのようですね。
+ * @immediate: patch all funcs immediately, bypassing safety mechanisms
他の修正個所としては、 linux-4.0
に存在していた struct klp_reloc
というライブパッチ時のバイナリ再配置情報を保持する構造体が linux-4.14.6
では struct klp_func
にまとめられています。
また、ソースコードが処理ごとに分けられています。これは機能追加というよりかは、単にリファクタリング的な対応のようです。
kernel/livepatch/core.c:
* void __weak arch_klp_init_object_loaded(struct klp_patch *patch,
* int klp_module_coming(struct module *mod)
* void klp_module_going(struct module *mod)
kernel/livepatch/patch.c:
* void klp_unpatch_object(struct klp_object *obj)
* int klp_patch_object(struct klp_object *obj)
* void klp_unpatch_objects(struct klp_patch *patch)
kernel/livepatch/transition.c:
* void klp_cancel_transition(void)
* void klp_update_patch_state(struct task_struct *task)
* void klp_try_complete_transition(void)
* void klp_start_transition(void)
* void klp_init_transition(struct klp_patch *patch, int state)
* void klp_reverse_transition(void)
* void klp_copy_process(struct task_struct *child)
ライブパッチを利用する手順は、 linux-4.0
の頃から変わっていない状態でした。
新機能とかは入っておらず、現状ではリファクタリングとパッチを当てる際のチェックまわりの処理が入っているという形になっています。
* klp_register_patch() でライブパッチを登録する。
* klp_enable_patch() でパッチを有効化する。
* klp_unregister_patch() でライブパッチを外す。
まとめ
linux-4.0
で追加されたライブパッチ機能が、最新版の linux-4.14.6
ではどうなっているかを調べてみました。
今回はソースコードの確認のみになっていますが、実際にサンプルを動かしてみるのも楽しそうです。