mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
selftests/livepatch: introduce tests
Add a few livepatch modules and simple target modules that the included regression suite can run tests against: - basic livepatching (multiple patches, atomic replace) - pre/post (un)patch callbacks - shadow variable API Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Tested-by: Miroslav Benes <mbenes@suse.cz> Tested-by: Alice Ferrazzi <alice.ferrazzi@gmail.com> Acked-by: Joe Lawrence <joe.lawrence@redhat.com> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
committed by
Jiri Kosina
parent
d67a537209
commit
a2818ee4dc
15
lib/livepatch/Makefile
Normal file
15
lib/livepatch/Makefile
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for livepatch test code.
|
||||
|
||||
obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
|
||||
test_klp_callbacks_demo.o \
|
||||
test_klp_callbacks_demo2.o \
|
||||
test_klp_callbacks_busy.o \
|
||||
test_klp_callbacks_mod.o \
|
||||
test_klp_livepatch.o \
|
||||
test_klp_shadow_vars.o
|
||||
|
||||
# Target modules to be livepatched require CC_FLAGS_FTRACE
|
||||
CFLAGS_test_klp_callbacks_busy.o += $(CC_FLAGS_FTRACE)
|
||||
CFLAGS_test_klp_callbacks_mod.o += $(CC_FLAGS_FTRACE)
|
57
lib/livepatch/test_klp_atomic_replace.c
Normal file
57
lib/livepatch/test_klp_atomic_replace.c
Normal file
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
|
||||
|
||||
#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)");
|
||||
|
||||
#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 in the init function below for demo purposes */
|
||||
};
|
||||
|
||||
static int test_klp_atomic_replace_init(void)
|
||||
{
|
||||
patch.replace = replace;
|
||||
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_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: atomic replace");
|
43
lib/livepatch/test_klp_callbacks_busy.c
Normal file
43
lib/livepatch/test_klp_callbacks_busy.c
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
static int sleep_secs;
|
||||
module_param(sleep_secs, int, 0644);
|
||||
MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)");
|
||||
|
||||
static void busymod_work_func(struct work_struct *work);
|
||||
static DECLARE_DELAYED_WORK(work, busymod_work_func);
|
||||
|
||||
static void busymod_work_func(struct work_struct *work)
|
||||
{
|
||||
pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs);
|
||||
msleep(sleep_secs * 1000);
|
||||
pr_info("%s exit\n", __func__);
|
||||
}
|
||||
|
||||
static int test_klp_callbacks_busy_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
schedule_delayed_work(&work,
|
||||
msecs_to_jiffies(1000 * 0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_klp_callbacks_busy_exit(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&work);
|
||||
pr_info("%s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(test_klp_callbacks_busy_init);
|
||||
module_exit(test_klp_callbacks_busy_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: busy target module");
|
121
lib/livepatch/test_klp_callbacks_demo.c
Normal file
121
lib/livepatch/test_klp_callbacks_demo.c
Normal file
@@ -0,0 +1,121 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
static int pre_patch_ret;
|
||||
module_param(pre_patch_ret, int, 0644);
|
||||
MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (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 pre_patch_ret;
|
||||
}
|
||||
|
||||
/* 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 void patched_work_func(struct work_struct *work)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
}
|
||||
|
||||
static struct klp_func no_funcs[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static struct klp_func busymod_funcs[] = {
|
||||
{
|
||||
.old_name = "busymod_work_func",
|
||||
.new_func = patched_work_func,
|
||||
}, {}
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
}, {
|
||||
.name = "test_klp_callbacks_mod",
|
||||
.funcs = no_funcs,
|
||||
.callbacks = {
|
||||
.pre_patch = pre_patch_callback,
|
||||
.post_patch = post_patch_callback,
|
||||
.pre_unpatch = pre_unpatch_callback,
|
||||
.post_unpatch = post_unpatch_callback,
|
||||
},
|
||||
}, {
|
||||
.name = "test_klp_callbacks_busy",
|
||||
.funcs = busymod_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 test_klp_callbacks_demo_init(void)
|
||||
{
|
||||
return klp_enable_patch(&patch);
|
||||
}
|
||||
|
||||
static void test_klp_callbacks_demo_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(test_klp_callbacks_demo_init);
|
||||
module_exit(test_klp_callbacks_demo_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_INFO(livepatch, "Y");
|
||||
MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: livepatch demo");
|
93
lib/livepatch/test_klp_callbacks_demo2.c
Normal file
93
lib/livepatch/test_klp_callbacks_demo2.c
Normal file
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
|
||||
|
||||
#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,
|
||||
/* set .replace in the init function below for demo purposes */
|
||||
};
|
||||
|
||||
static int test_klp_callbacks_demo2_init(void)
|
||||
{
|
||||
patch.replace = replace;
|
||||
return klp_enable_patch(&patch);
|
||||
}
|
||||
|
||||
static void test_klp_callbacks_demo2_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(test_klp_callbacks_demo2_init);
|
||||
module_exit(test_klp_callbacks_demo2_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_INFO(livepatch, "Y");
|
||||
MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: livepatch demo2");
|
24
lib/livepatch/test_klp_callbacks_mod.c
Normal file
24
lib/livepatch/test_klp_callbacks_mod.c
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
static int test_klp_callbacks_mod_init(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_klp_callbacks_mod_exit(void)
|
||||
{
|
||||
pr_info("%s\n", __func__);
|
||||
}
|
||||
|
||||
module_init(test_klp_callbacks_mod_init);
|
||||
module_exit(test_klp_callbacks_mod_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: target module");
|
51
lib/livepatch/test_klp_livepatch.c
Normal file
51
lib/livepatch/test_klp_livepatch.c
Normal file
@@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
static int livepatch_cmdline_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 = "cmdline_proc_show",
|
||||
.new_func = livepatch_cmdline_proc_show,
|
||||
}, { }
|
||||
};
|
||||
|
||||
static struct klp_object objs[] = {
|
||||
{
|
||||
/* name being NULL means vmlinux */
|
||||
.funcs = funcs,
|
||||
}, { }
|
||||
};
|
||||
|
||||
static struct klp_patch patch = {
|
||||
.mod = THIS_MODULE,
|
||||
.objs = objs,
|
||||
};
|
||||
|
||||
static int test_klp_livepatch_init(void)
|
||||
{
|
||||
return klp_enable_patch(&patch);
|
||||
}
|
||||
|
||||
static void test_klp_livepatch_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(test_klp_livepatch_init);
|
||||
module_exit(test_klp_livepatch_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_INFO(livepatch, "Y");
|
||||
MODULE_AUTHOR("Seth Jennings <sjenning@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: livepatch module");
|
236
lib/livepatch/test_klp_shadow_vars.c
Normal file
236
lib/livepatch/test_klp_shadow_vars.c
Normal file
@@ -0,0 +1,236 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2018 Joe Lawrence <joe.lawrence@redhat.com>
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/livepatch.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Keep a small list of pointers so that we can print address-agnostic
|
||||
* pointer values. Use a rolling integer count to differentiate the values.
|
||||
* Ironically we could have used the shadow variable API to do this, but
|
||||
* let's not lean too heavily on the very code we're testing.
|
||||
*/
|
||||
static LIST_HEAD(ptr_list);
|
||||
struct shadow_ptr {
|
||||
void *ptr;
|
||||
int id;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static void free_ptr_list(void)
|
||||
{
|
||||
struct shadow_ptr *sp, *tmp_sp;
|
||||
|
||||
list_for_each_entry_safe(sp, tmp_sp, &ptr_list, list) {
|
||||
list_del(&sp->list);
|
||||
kfree(sp);
|
||||
}
|
||||
}
|
||||
|
||||
static int ptr_id(void *ptr)
|
||||
{
|
||||
struct shadow_ptr *sp;
|
||||
static int count;
|
||||
|
||||
list_for_each_entry(sp, &ptr_list, list) {
|
||||
if (sp->ptr == ptr)
|
||||
return sp->id;
|
||||
}
|
||||
|
||||
sp = kmalloc(sizeof(*sp), GFP_ATOMIC);
|
||||
if (!sp)
|
||||
return -1;
|
||||
sp->ptr = ptr;
|
||||
sp->id = count++;
|
||||
|
||||
list_add(&sp->list, &ptr_list);
|
||||
|
||||
return sp->id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shadow variable wrapper functions that echo the function and arguments
|
||||
* to the kernel log for testing verification. Don't display raw pointers,
|
||||
* but use the ptr_id() value instead.
|
||||
*/
|
||||
static void *shadow_get(void *obj, unsigned long id)
|
||||
{
|
||||
void *ret = klp_shadow_get(obj, id);
|
||||
|
||||
pr_info("klp_%s(obj=PTR%d, id=0x%lx) = PTR%d\n",
|
||||
__func__, ptr_id(obj), id, ptr_id(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *shadow_alloc(void *obj, unsigned long id, size_t size,
|
||||
gfp_t gfp_flags, klp_shadow_ctor_t ctor,
|
||||
void *ctor_data)
|
||||
{
|
||||
void *ret = klp_shadow_alloc(obj, id, size, gfp_flags, ctor,
|
||||
ctor_data);
|
||||
pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n",
|
||||
__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
|
||||
ptr_id(ctor_data), ptr_id(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *shadow_get_or_alloc(void *obj, unsigned long id, size_t size,
|
||||
gfp_t gfp_flags, klp_shadow_ctor_t ctor,
|
||||
void *ctor_data)
|
||||
{
|
||||
void *ret = klp_shadow_get_or_alloc(obj, id, size, gfp_flags, ctor,
|
||||
ctor_data);
|
||||
pr_info("klp_%s(obj=PTR%d, id=0x%lx, size=%zx, gfp_flags=%pGg), ctor=PTR%d, ctor_data=PTR%d = PTR%d\n",
|
||||
__func__, ptr_id(obj), id, size, &gfp_flags, ptr_id(ctor),
|
||||
ptr_id(ctor_data), ptr_id(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void shadow_free(void *obj, unsigned long id, klp_shadow_dtor_t dtor)
|
||||
{
|
||||
klp_shadow_free(obj, id, dtor);
|
||||
pr_info("klp_%s(obj=PTR%d, id=0x%lx, dtor=PTR%d)\n",
|
||||
__func__, ptr_id(obj), id, ptr_id(dtor));
|
||||
}
|
||||
|
||||
static void shadow_free_all(unsigned long id, klp_shadow_dtor_t dtor)
|
||||
{
|
||||
klp_shadow_free_all(id, dtor);
|
||||
pr_info("klp_%s(id=0x%lx, dtor=PTR%d)\n",
|
||||
__func__, id, ptr_id(dtor));
|
||||
}
|
||||
|
||||
|
||||
/* Shadow variable constructor - remember simple pointer data */
|
||||
static int shadow_ctor(void *obj, void *shadow_data, void *ctor_data)
|
||||
{
|
||||
int **shadow_int = shadow_data;
|
||||
*shadow_int = ctor_data;
|
||||
pr_info("%s: PTR%d -> PTR%d\n",
|
||||
__func__, ptr_id(shadow_int), ptr_id(ctor_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shadow_dtor(void *obj, void *shadow_data)
|
||||
{
|
||||
pr_info("%s(obj=PTR%d, shadow_data=PTR%d)\n",
|
||||
__func__, ptr_id(obj), ptr_id(shadow_data));
|
||||
}
|
||||
|
||||
static int test_klp_shadow_vars_init(void)
|
||||
{
|
||||
void *obj = THIS_MODULE;
|
||||
int id = 0x1234;
|
||||
size_t size = sizeof(int *);
|
||||
gfp_t gfp_flags = GFP_KERNEL;
|
||||
|
||||
int var1, var2, var3, var4;
|
||||
int **sv1, **sv2, **sv3, **sv4;
|
||||
|
||||
void *ret;
|
||||
|
||||
ptr_id(NULL);
|
||||
ptr_id(&var1);
|
||||
ptr_id(&var2);
|
||||
ptr_id(&var3);
|
||||
ptr_id(&var4);
|
||||
|
||||
/*
|
||||
* With an empty shadow variable hash table, expect not to find
|
||||
* any matches.
|
||||
*/
|
||||
ret = shadow_get(obj, id);
|
||||
if (!ret)
|
||||
pr_info(" got expected NULL result\n");
|
||||
|
||||
/*
|
||||
* Allocate a few shadow variables with different <obj> and <id>.
|
||||
*/
|
||||
sv1 = shadow_alloc(obj, id, size, gfp_flags, shadow_ctor, &var1);
|
||||
sv2 = shadow_alloc(obj + 1, id, size, gfp_flags, shadow_ctor, &var2);
|
||||
sv3 = shadow_alloc(obj, id + 1, size, gfp_flags, shadow_ctor, &var3);
|
||||
|
||||
/*
|
||||
* Verify we can find our new shadow variables and that they point
|
||||
* to expected data.
|
||||
*/
|
||||
ret = shadow_get(obj, id);
|
||||
if (ret == sv1 && *sv1 == &var1)
|
||||
pr_info(" got expected PTR%d -> PTR%d result\n",
|
||||
ptr_id(sv1), ptr_id(*sv1));
|
||||
ret = shadow_get(obj + 1, id);
|
||||
if (ret == sv2 && *sv2 == &var2)
|
||||
pr_info(" got expected PTR%d -> PTR%d result\n",
|
||||
ptr_id(sv2), ptr_id(*sv2));
|
||||
ret = shadow_get(obj, id + 1);
|
||||
if (ret == sv3 && *sv3 == &var3)
|
||||
pr_info(" got expected PTR%d -> PTR%d result\n",
|
||||
ptr_id(sv3), ptr_id(*sv3));
|
||||
|
||||
/*
|
||||
* Allocate or get a few more, this time with the same <obj>, <id>.
|
||||
* The second invocation should return the same shadow var.
|
||||
*/
|
||||
sv4 = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
|
||||
ret = shadow_get_or_alloc(obj + 2, id, size, gfp_flags, shadow_ctor, &var4);
|
||||
if (ret == sv4 && *sv4 == &var4)
|
||||
pr_info(" got expected PTR%d -> PTR%d result\n",
|
||||
ptr_id(sv4), ptr_id(*sv4));
|
||||
|
||||
/*
|
||||
* Free the <obj=*, id> shadow variables and check that we can no
|
||||
* longer find them.
|
||||
*/
|
||||
shadow_free(obj, id, shadow_dtor); /* sv1 */
|
||||
ret = shadow_get(obj, id);
|
||||
if (!ret)
|
||||
pr_info(" got expected NULL result\n");
|
||||
|
||||
shadow_free(obj + 1, id, shadow_dtor); /* sv2 */
|
||||
ret = shadow_get(obj + 1, id);
|
||||
if (!ret)
|
||||
pr_info(" got expected NULL result\n");
|
||||
|
||||
shadow_free(obj + 2, id, shadow_dtor); /* sv4 */
|
||||
ret = shadow_get(obj + 2, id);
|
||||
if (!ret)
|
||||
pr_info(" got expected NULL result\n");
|
||||
|
||||
/*
|
||||
* We should still find an <id+1> variable.
|
||||
*/
|
||||
ret = shadow_get(obj, id + 1);
|
||||
if (ret == sv3 && *sv3 == &var3)
|
||||
pr_info(" got expected PTR%d -> PTR%d result\n",
|
||||
ptr_id(sv3), ptr_id(*sv3));
|
||||
|
||||
/*
|
||||
* Free all the <id+1> variables, too.
|
||||
*/
|
||||
shadow_free_all(id + 1, shadow_dtor); /* sv3 */
|
||||
ret = shadow_get(obj, id);
|
||||
if (!ret)
|
||||
pr_info(" shadow_get() got expected NULL result\n");
|
||||
|
||||
|
||||
free_ptr_list();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test_klp_shadow_vars_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(test_klp_shadow_vars_init);
|
||||
module_exit(test_klp_shadow_vars_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Joe Lawrence <joe.lawrence@redhat.com>");
|
||||
MODULE_DESCRIPTION("Livepatch test: shadow variables");
|
Reference in New Issue
Block a user