From: Roman Rashchupkin <raschupkin.ri@xxxxxxxxx> Signed-off-by: Roman Rashchupkin <raschupkin.ri@xxxxxxxxx> --- tools/testing/selftests/livepatch/Makefile | 3 +- .../selftests/livepatch/test-kprefcount.sh | 16 +++ .../selftests/livepatch/test_modules/Makefile | 4 +- .../test_modules/test_klp_kprefcount.c | 120 ++++++++++++++++++ .../test_modules/test_klp_refcount.c | 65 ++++++++++ 5 files changed, 206 insertions(+), 2 deletions(-) create mode 100755 tools/testing/selftests/livepatch/test-kprefcount.sh create mode 100644 tools/testing/selftests/livepatch/test_modules/test_klp_kprefcount.c create mode 100644 tools/testing/selftests/livepatch/test_modules/test_klp_refcount.c diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile index 35418a4790be..48926ebc77f2 100644 --- a/tools/testing/selftests/livepatch/Makefile +++ b/tools/testing/selftests/livepatch/Makefile @@ -10,7 +10,8 @@ TEST_PROGS := \ test-state.sh \ test-ftrace.sh \ test-sysfs.sh \ - test-syscall.sh + test-syscall.sh \ + test-kprefcount.sh TEST_FILES := settings diff --git a/tools/testing/selftests/livepatch/test-kprefcount.sh b/tools/testing/selftests/livepatch/test-kprefcount.sh new file mode 100755 index 000000000000..8ea6c18f59dd --- /dev/null +++ b/tools/testing/selftests/livepatch/test-kprefcount.sh @@ -0,0 +1,16 @@ +#!/bin/sh +insmod test_modules/test_klp_refcount.ko +insmod test_modules/test_klp_kprefcount.ko +livepatch_enabled=/sys/kernel/livepatch/test_klp_kprefcount/enabled +while [ ! -e $livepatch_enabled -o $(cat $livepatch_enabled) -eq 0 ]; do + sleep 0.01; +done +echo 0 > $livepatch_enabled +while [ $(cat $livepatch_enabled) -eq 1 ]; do + sleep 0.01; +done +while [ -e $livepatch_enabled ]; do + sleep 0.01; +done +rmmod test_klp_kprefcount +rmmod test_klp_refcount diff --git a/tools/testing/selftests/livepatch/test_modules/Makefile b/tools/testing/selftests/livepatch/test_modules/Makefile index e6e638c4bcba..c26797372e0d 100644 --- a/tools/testing/selftests/livepatch/test_modules/Makefile +++ b/tools/testing/selftests/livepatch/test_modules/Makefile @@ -11,7 +11,9 @@ obj-m += test_klp_atomic_replace.o \ test_klp_state2.o \ test_klp_state3.o \ test_klp_shadow_vars.o \ - test_klp_syscall.o + test_klp_syscall.o \ + test_klp_refcount.o \ + test_klp_kprefcount.o # Ensure that KDIR exists, otherwise skip the compilation modules: diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_kprefcount.c b/tools/testing/selftests/livepatch/test_modules/test_klp_kprefcount.c new file mode 100644 index 000000000000..063f286ebcec --- /dev/null +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_kprefcount.c @@ -0,0 +1,120 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/livepatch.h> +#include <linux/livepatch_refcount.h> + +extern refcount_t test_refcount; +extern int TEST_LIVEPATCH_KPREFCOUNT; + +#define ITER 100 +#define NREF 10 +#define KP_NREF 10 +static struct ref_holder { + unsigned char v; +} kp_ref_holders[KP_NREF] = { 0 }; +kprefcount_t *kp_test_ref = 0; + +static int livepatch_refcount_test_iter(void) +{ + int k, i; + for (k=0; k<ITER; k++) { + for (i=0; i<KP_NREF; i++) + refcount_inc(&test_refcount); + for (i=0; i<KP_NREF; i++) + if (kprefcount_dec_and_test(kp_test_ref, 0, 1)) { + pr_alert("livepatch refcount underflow\n"); + return -1; + } + } + TEST_LIVEPATCH_KPREFCOUNT = 0; + return 0; +} + +struct delayed_work kp_work_refcount; +static void kp_test_refcount(struct work_struct *work) +{ + int i, k; + for (k=0; k<ITER; k++) { + for (i=0; i<KP_NREF; i++) + kprefcount_dec(kp_test_ref, &kp_ref_holders[i].v, 1); + // Intentional refcounter underflow for additional testing + for (i=0; i<KP_NREF-1; i++) + kprefcount_inc(kp_test_ref, &kp_ref_holders[i].v, 1); + } +} + +static void kp_post_patch_callback(struct klp_object *klp_obj) +{ + schedule_delayed_work(&kp_work_refcount, 0); +} + +static void kp_pre_unpatch_callback(struct klp_object *klp_obj) +{ + cancel_delayed_work_sync(&kp_work_refcount); +} + +static struct klp_func funcs[] = { + { + .old_name = "refcount_test_iter", + .new_func = livepatch_refcount_test_iter, + }, { } +}; + +static struct klp_object objs[] = { + { + .name = "test_klp_refcount", + .funcs = funcs, + .callbacks = { + .post_patch = kp_post_patch_callback, + .pre_unpatch = kp_pre_unpatch_callback, + }, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +struct delayed_work work_refcount; + +static void do_test_refcount(struct work_struct *work) +{ + int i; + for (i=0; i<NREF; i++) { + if (refcount_read(&test_refcount) <= 1) + pr_info("LIVEPATCH refcount test done.\n"); + return; + refcount_dec(&test_refcount); + if (refcount_read(&test_refcount) < 0) + pr_alert("post-livepatch refcount underflow\n"); + } + for (i=0; i<NREF; i++) + refcount_inc(&test_refcount); +} + +static int refcount_test_init(void) +{ + int ret; + kp_test_ref = kprefcount_alloc(&test_refcount, GFP_KERNEL); + if (!kp_test_ref) { + pr_alert("kprefcount_livepatch: memory allocation_failed"); + return -1; + } + ret = klp_enable_patch(&patch); + INIT_DELAYED_WORK(&kp_work_refcount, kp_test_refcount); + return 0; +} + +static void refcount_test_exit(void) +{ +} + +module_init(refcount_test_init); +module_exit(refcount_test_exit); +MODULE_INFO(livepatch, "Y"); +MODULE_AUTHOR("Roman Rashchupkin <raschupkin.ri@xxxxxxxxx>"); +MODULE_DESCRIPTION("Livepatch test: kprefcount"); +MODULE_LICENSE("GPL"); diff --git a/tools/testing/selftests/livepatch/test_modules/test_klp_refcount.c b/tools/testing/selftests/livepatch/test_modules/test_klp_refcount.c new file mode 100644 index 000000000000..bd9c57e63476 --- /dev/null +++ b/tools/testing/selftests/livepatch/test_modules/test_klp_refcount.c @@ -0,0 +1,65 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/livepatch.h> +#include <linux/livepatch_refcount.h> + +#define ITER 100 +#define NREF 10 +int TEST_LIVEPATCH_KPREFCOUNT = 1; +EXPORT_SYMBOL(TEST_LIVEPATCH_KPREFCOUNT); +refcount_t test_refcount = REFCOUNT_INIT(1); +EXPORT_SYMBOL(test_refcount); + +int refcount_test_iter(void) +{ + int i; + for (i=0; i<NREF; i++) + refcount_inc(&test_refcount); + for (i=0; i<NREF; i++) + if (refcount_dec_and_test(&test_refcount)) + return -1; + return 0; +} + +int refcount_test(void) +{ + int i; + for (i=0; i<ITER; i++) { + if (refcount_test_iter()) + return -1; + } + return 0; +} +EXPORT_SYMBOL(refcount_test); + +struct delayed_work kp_test_work; +void start_refcount_test(struct work_struct *work) +{ + if (!TEST_LIVEPATCH_KPREFCOUNT) + return; + if (refcount_test()) { + pr_alert(KERN_ERR "refcount_test: error.\n"); + return; + } + schedule_delayed_work(&kp_test_work, msecs_to_jiffies(50)); +} + +static int refcount_test_init(void) +{ + INIT_DELAYED_WORK(&kp_test_work, start_refcount_test); + schedule_delayed_work(&kp_test_work, msecs_to_jiffies(50)); + return 0; +} + +static void refcount_test_exit(void) +{ + cancel_delayed_work_sync(&kp_test_work); +} + +module_init(refcount_test_init); +module_exit(refcount_test_exit); +MODULE_AUTHOR("Roman Rashchupkin <raschupkin.ri@xxxxxxxxx>"); +MODULE_DESCRIPTION("Livepatch test: refcount"); +MODULE_LICENSE("GPL"); -- 2.43.0