This patch is only an example of how to generate #GP by IBT. It serves only as an example for those wondering how the Linux behaves in this case. Don't merge this patch, because it causes a refcount underflow of the test module, so it's not possible to unload it. Signed-off-by: Lukas Hruska <lhruska@xxxxxxx> --- tools/testing/selftests/livepatch/Makefile | 4 ++ tools/testing/selftests/livepatch/test-ibt.sh | 57 +++++++++++++++++++ .../selftests/livepatch/test_modules/Makefile | 2 + .../test_modules/test_klp_extern_hello.c | 20 +++++++ .../livepatch/test_modules/test_klp_ibt.c | 51 +++++++++++++++++ 5 files changed, 134 insertions(+) create mode 100644 tools/testing/selftests/livepatch/test-ibt.sh create mode 100644 tools/testing/selftests/livepatch/test_modules/test_klp_ibt.c diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile index 611ee16bef56..3ef2040e4c50 100644 --- a/tools/testing/selftests/livepatch/Makefile +++ b/tools/testing/selftests/livepatch/Makefile @@ -13,6 +13,10 @@ TEST_PROGS := \ test-syscall.sh \ test-extern.sh +ifdef CONFIG_X86_KERNEL_IBT + TEST_PROGS += test-ibt.sh +endif + TEST_FILES := settings include ../lib.mk diff --git a/tools/testing/selftests/livepatch/test-ibt.sh b/tools/testing/selftests/livepatch/test-ibt.sh new file mode 100644 index 000000000000..c5f49fb7af4d --- /dev/null +++ b/tools/testing/selftests/livepatch/test-ibt.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2024 Lukas Hruska <lhruska@xxxxxxx> + +. $(dirname $0)/functions.sh + +MOD_LIVEPATCH=test_klp_ibt +MOD_HELLO=test_klp_extern_hello +PARAM_HELLO=hello + +setup_config + +# - load a module to be livepatched +# - load a livepatch that calls the unused function which does not have endbr64 +# as its first instruction +# - unload the livepatch and make sure the patch was removed +# - unload the module that was livepatched + +start_test "livepatch with external symbol" + +load_mod $MOD_HELLO + +read_module_param $MOD_HELLO $PARAM_HELLO + +load_lp $MOD_LIVEPATCH + +read_module_param $MOD_HELLO $PARAM_HELLO + +disable_lp $MOD_LIVEPATCH +unload_lp $MOD_LIVEPATCH + +read_module_param $MOD_HELLO $PARAM_HELLO + +unload_mod $MOD_HELLO + +check_result "% insmod test_modules/$MOD_HELLO.ko +% echo \"$MOD_HELLO/parameters/$PARAM_HELLO: \$(cat /sys/module/$MOD_HELLO/parameters/$PARAM_HELLO)\" +$MOD_HELLO/parameters/$PARAM_HELLO: Hello from kernel module. +% insmod test_modules/$MOD_LIVEPATCH.ko +livepatch: enabling patch '$MOD_LIVEPATCH' +livepatch: '$MOD_LIVEPATCH': initializing patching transition +livepatch: '$MOD_LIVEPATCH': starting patching transition +livepatch: '$MOD_LIVEPATCH': completing patching transition +livepatch: '$MOD_LIVEPATCH': patching complete +% echo \"$MOD_HELLO/parameters/$PARAM_HELLO: \$(cat /sys/module/$MOD_HELLO/parameters/$PARAM_HELLO)\" +$MOD_HELLO/parameters/$PARAM_HELLO: Hello from livepatched module. +% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled +livepatch: '$MOD_LIVEPATCH': initializing unpatching transition +livepatch: '$MOD_LIVEPATCH': starting unpatching transition +livepatch: '$MOD_LIVEPATCH': completing unpatching transition +livepatch: '$MOD_LIVEPATCH': unpatching complete +% rmmod $MOD_LIVEPATCH +% echo \"$MOD_HELLO/parameters/$PARAM_HELLO: \$(cat /sys/module/$MOD_HELLO/parameters/$PARAM_HELLO)\" +$MOD_HELLO/parameters/$PARAM_HELLO: Hello from kernel module. +% rmmod $MOD_HELLO" + +exit 0 diff --git a/tools/testing/selftests/livepatch/test_modules/Makefile b/tools/testing/selftests/livepatch/test_modules/Makefile index 0d6df14787da..49a22ea90f3a 100644 --- a/tools/testing/selftests/livepatch/test_modules/Makefile +++ b/tools/testing/selftests/livepatch/test_modules/Makefile @@ -15,6 +15,8 @@ obj-m += test_klp_atomic_replace.o \ test_klp_shadow_vars.o \ test_klp_syscall.o +obj-$(CONFIG_X86_KERNEL_IBT) += test_klp_ibt.o + # Ensure that KDIR exists, otherwise skip the compilation modules: ifneq ("$(wildcard $(KDIR))", "") diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_extern_hello.c b/tools/testing/selftests/livepatch/test_modules/test_klp_extern_hello.c index 431c55b5849a..37e1cd2cecdb 100644 --- a/tools/testing/selftests/livepatch/test_modules/test_klp_extern_hello.c +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_extern_hello.c @@ -13,6 +13,26 @@ static int hello_get(char *buffer, const struct kernel_param *kp) return sysfs_emit(buffer, "%s kernel module.\n", hello_msg); } +#ifdef CONFIG_X86_KERNEL_IBT +static __attribute__((nocf_check)) int hello_get_alt(char *buffer, const struct kernel_param *kp) +{ + return sysfs_emit(buffer, "%s unused function.\n", hello_msg); +} + +static int fail_get(char *buffer, const struct kernel_param *kp) +{ + int __attribute__((nocf_check)) (* volatile klpe_hello_get_alt)(char *, const struct kernel_param *) = hello_get_alt; + return (*klpe_hello_get_alt)(buffer, kp); +} + +static const struct kernel_param_ops fail_ops = { + .get = fail_get, +}; + +module_param_cb(fail, &fail_ops, NULL, 0400); +MODULE_PARM_DESC(fail, "Read only parameter failing the reader."); +#endif + static const struct kernel_param_ops hello_ops = { .get = hello_get, }; diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_ibt.c b/tools/testing/selftests/livepatch/test_modules/test_klp_ibt.c new file mode 100644 index 000000000000..3b76d175d398 --- /dev/null +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_ibt.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2024 Lukas Hruska <lhruska@xxxxxxx> + +#define pr_fmt(fmt) "test_klp_extern_hello: " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/livepatch.h> + +extern int hello_get_alt(char *buffer, const struct kernel_param *kp) + KLP_RELOC_SYMBOL(test_klp_extern_hello, test_klp_extern_hello, hello_get_alt); + +static int hello_get(char *buffer, const struct kernel_param *kp) +{ + return hello_get_alt(buffer, kp); +} + +static struct klp_func funcs[] = { + { + .old_name = "hello_get", + .new_func = hello_get, + }, { } +}; + +static struct klp_object objs[] = { + { + .name = "test_klp_extern_hello", + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int test_klp_extern_init(void) +{ + return klp_enable_patch(&patch); +} + +static void test_klp_extern_exit(void) +{ +} + +module_init(test_klp_extern_init); +module_exit(test_klp_extern_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); +MODULE_AUTHOR("Lukas Hruska <lhruska@xxxxxxx>"); +MODULE_DESCRIPTION("Livepatch test: external function call with IBT enabled"); -- 2.46.0