Update livepatch callback documentation and samples with respect to new atomic replace / cumulative patch functionality. Signed-off-by: Joe Lawrence <joe.lawrence@xxxxxxxxxx> --- Documentation/livepatch/callbacks.txt | 102 ++++++++++++++++ samples/livepatch/Makefile | 1 + samples/livepatch/livepatch-callbacks-demo2.c | 162 ++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 samples/livepatch/livepatch-callbacks-demo2.c diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt index c9776f48e458..b5e67975c5a9 100644 --- a/Documentation/livepatch/callbacks.txt +++ b/Documentation/livepatch/callbacks.txt @@ -86,6 +86,13 @@ If the object did successfully patch, but the patch transition never started for some reason (e.g., if another object failed to patch), only the post-unpatch callback will be called. +If a livepatch is replaced by a cumulative patch, then only the +callbacks belonging to the cumulative patch will be executed. This +simplifies the livepatching core for it is the responsibility of the +cumulative patch to safely revert whatever needs to be reverted. See +Documentation/livepatch/cumulative.txt for more information on such +patches. + Example Use-cases ================= @@ -603,3 +610,98 @@ pre-unpatch callbacks are skipped: % rmmod samples/livepatch/livepatch-callbacks-busymod.ko [ 141.279111] livepatch_callbacks_busymod: busymod_work_func exit [ 141.279760] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit + + +Test 10 +------- + +Test loading multiple livepatch modules containing callback routines. +The livepatching core executes callbacks for all modules. + +- load livepatch +- load second livepatch +- disable livepatch +- disable second livepatch +- unload livepatch +- unload second livepatch + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 216.448208] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 216.448211] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 216.448330] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 216.448341] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 218.720099] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 218.720179] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 218.720180] livepatch: 'livepatch_callbacks_demo': patching complete + + % insmod samples/livepatch/livepatch-callbacks-demo2.ko + [ 220.126552] livepatch: enabling patch 'livepatch_callbacks_demo2' + [ 220.126554] livepatch: 'livepatch_callbacks_demo2': initializing patching transition + [ 220.126592] livepatch_callbacks_demo2: pre_patch_callback: vmlinux + [ 220.126593] livepatch: 'livepatch_callbacks_demo2': starting patching transition + [ 221.728091] livepatch: 'livepatch_callbacks_demo2': completing patching transition + [ 221.728254] livepatch_callbacks_demo2: post_patch_callback: vmlinux + [ 221.728255] livepatch: 'livepatch_callbacks_demo2': patching complete + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled + [ 223.434556] livepatch: 'livepatch_callbacks_demo2': initializing unpatching transition + [ 223.434616] livepatch_callbacks_demo2: pre_unpatch_callback: vmlinux + [ 223.434617] livepatch: 'livepatch_callbacks_demo2': starting unpatching transition + [ 224.736159] livepatch: 'livepatch_callbacks_demo2': completing unpatching transition + [ 224.736660] livepatch_callbacks_demo2: post_unpatch_callback: vmlinux + [ 224.736662] livepatch: 'livepatch_callbacks_demo2': unpatching complete + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 227.284070] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 227.284111] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 227.284112] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 228.704142] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 228.704215] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 228.704216] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo2.ko + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 11 +------- + +A similar test as the previous one, except this time load the second +callback demo module as a cumulative (ie, replacement) patch. The +livepatching core will only execute klp_object callbacks for the latest +cumulative patch on the patch stack. + +- load livepatch +- load second livepatch (atomic replace) +- disable livepatch +- disable second livepatch +- unload livepatch +- unload second livepatch + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [16435.711175] livepatch: enabling patch 'livepatch_callbacks_demo' + [16435.711185] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [16435.711271] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [16435.711297] livepatch: 'livepatch_callbacks_demo': starting patching transition + [16436.704092] livepatch: 'livepatch_callbacks_demo': completing patching transition + [16436.704363] livepatch_callbacks_demo: post_patch_callback: vmlinux + [16436.704364] livepatch: 'livepatch_callbacks_demo': patching complete + + % insmod samples/livepatch/livepatch-callbacks-demo2.ko replace=1 + [16442.760963] livepatch: enabling patch 'livepatch_callbacks_demo2' + [16442.760966] livepatch: 'livepatch_callbacks_demo2': initializing patching transition + [16442.761018] livepatch_callbacks_demo2: pre_patch_callback: vmlinux + [16442.761018] livepatch: 'livepatch_callbacks_demo2': starting patching transition + [16444.704092] livepatch: 'livepatch_callbacks_demo2': completing patching transition + [16444.704181] livepatch_callbacks_demo2: post_patch_callback: vmlinux + [16444.704181] livepatch: 'livepatch_callbacks_demo2': patching complete + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled + [16448.434672] livepatch: 'livepatch_callbacks_demo2': initializing unpatching transition + [16448.434712] livepatch: 'livepatch_callbacks_demo2': starting unpatching transition + [16449.760134] livepatch: 'livepatch_callbacks_demo2': completing unpatching transition + [16449.760338] livepatch: 'livepatch_callbacks_demo2': unpatching complete + ** TODO ** where are the demo2 unpatch callbacks? + + % rmmod samples/livepatch/livepatch-callbacks-demo2.ko + % rmmod samples/livepatch/livepatch-callbacks-demo.ko diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index dd0e2a8af1af..9fb4d9b845bb 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo2.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-cumulative.o diff --git a/samples/livepatch/livepatch-callbacks-demo2.c b/samples/livepatch/livepatch-callbacks-demo2.c new file mode 100644 index 000000000000..bc07ba7e0568 --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-demo2.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2018 Joe Lawrence <joe.lawrence@xxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * livepatch-callbacks-demo2.c - (un)patching callbacks livepatch demo + * + * + * Purpose + * ------- + * + * Demonstration of registering livepatch (un)patching callbacks and + * their behavior in cumulative patches. + * + * + * Usage + * ----- + * + * Step 1 - load two livepatch callback demos (default behavior) + * + * insmod samples/livepatch/livepatch-callbacks-demo.ko + * insmod samples/livepatch/livepatch-callbacks-demo2.ko replace=0 + * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled + * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + * + * Watch dmesg output to see pre and post (un)patch callbacks made for + * both livepatch-callbacks-demo and livepatch-callbacks-demo2. + * + * Remove the modules to prepare for the next step: + * + * rmmod samples/livepatch/livepatch-callbacks-demo2.ko + * rmmod samples/livepatch/livepatch-callbacks-demo.ko + * + * Step 1 - load two livepatch callback demos (cumulative behavior) + * + * insmod samples/livepatch/livepatch-callbacks-demo.ko + * insmod samples/livepatch/livepatch-callbacks-demo2.ko replace=1 + * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo2/enabled + * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + * + * Check dmesg output again and notice that when a cumulative patch is + * loaded, only its pre and post unpatch callbacks are executed. + * + * Final cleanup: + * + * rmmod samples/livepatch/livepatch-callbacks-demo2.ko + * rmmod samples/livepatch/livepatch-callbacks-demo.ko + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/livepatch.h> + +static int replace; +module_param(replace, int, 0644); +MODULE_PARM_DESC(replace, "replace (default=0)"); + +static const char *const module_state[] = { + [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", + [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", + [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", + [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", +}; + +static void callback_info(const char *callback, struct klp_object *obj) +{ + if (obj->mod) + pr_info("%s: %s -> %s\n", callback, obj->mod->name, + module_state[obj->mod->state]); + else + pr_info("%s: vmlinux\n", callback); +} + +/* Executed on object patching (ie, patch enablement) */ +static int pre_patch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); + return 0; +} + +/* Executed on object unpatching (ie, patch disablement) */ +static void post_patch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); +} + +/* Executed on object unpatching (ie, patch disablement) */ +static void pre_unpatch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); +} + +/* Executed on object unpatching (ie, patch disablement) */ +static void post_unpatch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); +} + +static struct klp_func no_funcs[] = { + { } +}; + +static struct klp_object objs[] = { + { + .name = NULL, /* vmlinux */ + .funcs = no_funcs, + .callbacks = { + .pre_patch = pre_patch_callback, + .post_patch = post_patch_callback, + .pre_unpatch = pre_unpatch_callback, + .post_unpatch = post_unpatch_callback, + }, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int livepatch_callbacks_demo2_init(void) +{ + int ret; + + patch.replace = replace; + + ret = klp_register_patch(&patch); + if (ret) + return ret; + ret = klp_enable_patch(&patch); + if (ret) { + WARN_ON(klp_unregister_patch(&patch)); + return ret; + } + return 0; +} + +static void livepatch_callbacks_demo2_exit(void) +{ + WARN_ON(klp_unregister_patch(&patch)); +} + +module_init(livepatch_callbacks_demo2_init); +module_exit(livepatch_callbacks_demo2_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe live-patching" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html