Livepatches with different replace_sets must not modify the same function. If a second livepatch attempts to modify a function that has already been modified by a loaded livepatch with a different replace_set, the loading will fail. if the second livepatch shares the same replace_set, it will load successfully.
Add a test case to verify this behavior. The test result is as follows: $ ./test-livepatch.sh TEST: basic function patching ... ok TEST: multiple livepatches ... ok TEST: module function patching ... ok TEST: module function patching (livepatch first) ... ok TEST: function conflict: different replace_sets ... ok <<<< TEST: function conflict: the same replace_set ... ok <<<< Signed-off-by: Yafang Shao <[email protected]> --- .../selftests/livepatch/test-livepatch.sh | 59 +++++++++++++++++++ .../selftests/livepatch/test_modules/Makefile | 1 + .../test_modules/test_klp_atomic_replace2.c | 55 +++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace2.c diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh index 4f8fb5620c6e..9cdfc048d71c 100755 --- a/tools/testing/selftests/livepatch/test-livepatch.sh +++ b/tools/testing/selftests/livepatch/test-livepatch.sh @@ -6,6 +6,7 @@ MOD_LIVEPATCH1=test_klp_livepatch MOD_REPLACE=test_klp_atomic_replace +MOD_REPLACE2=test_klp_atomic_replace2 MOD_TARGET=test_klp_mod_target MOD_TARGET_PATCH=test_klp_mod_patch @@ -203,5 +204,63 @@ livepatch: '$MOD_TARGET_PATCH': unpatching complete % rmmod $MOD_TARGET $MOD_TARGET: test_klp_mod_target_exit" +# - load a livepatch that modifies function A +# - loading another livepatch with a different replace_set that modifies +# the same function will fail + +start_test "function conflict: different replace_sets" + +load_lp $MOD_REPLACE replace_set=0 +load_failing_mod $MOD_REPLACE2 replace_set=1 +disable_lp $MOD_REPLACE +unload_lp $MOD_REPLACE + +check_result "% insmod test_modules/$MOD_REPLACE.ko replace_set=0 +livepatch: enabling patch '$MOD_REPLACE' +livepatch: '$MOD_REPLACE': initializing patching transition +livepatch: '$MOD_REPLACE': starting patching transition +livepatch: '$MOD_REPLACE': completing patching transition +livepatch: '$MOD_REPLACE': patching complete +% insmod test_modules/$MOD_REPLACE2.ko replace_set=1 +livepatch: Livepatch patch ($MOD_REPLACE2) is not compatible with the already installed livepatches. +insmod: ERROR: could not insert module test_modules/$MOD_REPLACE2.ko: Invalid parameters +% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled +livepatch: '$MOD_REPLACE': initializing unpatching transition +livepatch: '$MOD_REPLACE': starting unpatching transition +livepatch: '$MOD_REPLACE': completing unpatching transition +livepatch: '$MOD_REPLACE': unpatching complete +% rmmod $MOD_REPLACE" + +# - load a livepatch that modifies function A +# - loading another livepatch with the same replace_set that modifies +# the same function will succeed + +start_test "function conflict: the same replace_set" + +load_lp $MOD_REPLACE replace_set=0 +load_lp $MOD_REPLACE2 replace_set=0 +disable_lp $MOD_REPLACE2 +unload_lp $MOD_REPLACE2 +unload_lp $MOD_REPLACE + +check_result "% insmod test_modules/$MOD_REPLACE.ko replace_set=0 +livepatch: enabling patch '$MOD_REPLACE' +livepatch: '$MOD_REPLACE': initializing patching transition +livepatch: '$MOD_REPLACE': starting patching transition +livepatch: '$MOD_REPLACE': completing patching transition +livepatch: '$MOD_REPLACE': patching complete +% insmod test_modules/$MOD_REPLACE2.ko replace_set=0 +livepatch: enabling patch '$MOD_REPLACE2' +livepatch: '$MOD_REPLACE2': initializing patching transition +livepatch: '$MOD_REPLACE2': starting patching transition +livepatch: '$MOD_REPLACE2': completing patching transition +livepatch: '$MOD_REPLACE2': patching complete +% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE2/enabled +livepatch: '$MOD_REPLACE2': initializing unpatching transition +livepatch: '$MOD_REPLACE2': starting unpatching transition +livepatch: '$MOD_REPLACE2': completing unpatching transition +livepatch: '$MOD_REPLACE2': unpatching complete +% rmmod $MOD_REPLACE2 +% rmmod $MOD_REPLACE" exit 0 diff --git a/tools/testing/selftests/livepatch/test_modules/Makefile b/tools/testing/selftests/livepatch/test_modules/Makefile index bdc5ae37311e..f7111d500cab 100644 --- a/tools/testing/selftests/livepatch/test_modules/Makefile +++ b/tools/testing/selftests/livepatch/test_modules/Makefile @@ -2,6 +2,7 @@ TESTMODS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) KDIR ?= /lib/modules/$(shell uname -r)/build obj-m += test_klp_atomic_replace.o \ + test_klp_atomic_replace2.o \ test_klp_callbacks_busy.o \ test_klp_callbacks_demo.o \ test_klp_callbacks_demo2.o \ diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace2.c b/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace2.c new file mode 100644 index 000000000000..1dff8b6d45ab --- /dev/null +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_atomic_replace2.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/livepatch.h> + +static int replace_set; +module_param(replace_set, int, 0644); +MODULE_PARM_DESC(replace_set, "replace_set (default=0)"); + +#include <linux/seq_file.h> +static int livepatch_meminfo_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s: %s\n", THIS_MODULE->name, + "this has been live patched"); + return 0; +} + +static struct klp_func funcs[] = { + { + .old_name = "meminfo_proc_show", + .new_func = livepatch_meminfo_proc_show, + }, {} +}; + +static struct klp_object objs[] = { + { + /* name being NULL means vmlinux */ + .funcs = funcs, + }, {} +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, + /* set .replace_set in the init function below for demo purposes */ +}; + +static int test_klp_atomic_replace_init(void) +{ + patch.replace_set = replace_set; + return klp_enable_patch(&patch); +} + +static void test_klp_atomic_replace_exit(void) +{ +} + +module_init(test_klp_atomic_replace_init); +module_exit(test_klp_atomic_replace_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); +MODULE_DESCRIPTION("Livepatch test: modified function compatibility verification"); -- 2.52.0
