Merge: livepatch: selected fixes for rhel-9.6

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/5428

JIRA: https://issues.redhat.com/browse/RHEL-61781

A collection of small bug fixes and testing updates to the livepatch
subsystem as requested by client

Conflicts:

6a71770442b5 (selftests: livepatch: Test livepatching a heavily called syscall):
- Moved (test_klp-call_getpid.c, test_klp_syscall.c) to /lib/livepatch/
from /tools/testing/selftests/livepatch/
- Edited lib/livepatch/Makefile to compile the new files:
(test_klp-call_getpid.c, test_klp_syscall.c

61894818e304 (selftests: livepatch: Test atomic replace against multiple modules):
- Changed the module insertion from 'insmod' -> 'modprobe' to not break current testing setup

Omitted:

54ee3526796f (selftests: livepatch: Avoid running the tests if kernel-devel is missing):

- Would require c4bbe83d27c2 ("livepatch: Move tests from lib/livepatch to selftests/livepatch")
which breaks our current testing setup 

Signed-off-by: Ryan Sullivan <rysulliv@redhat.com>

Approved-by: Chris von Recklinghausen <crecklin@redhat.com>
Approved-by: Joe Lawrence <joe.lawrence@redhat.com>
Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com>

Merged-by: Rado Vrbovsky <rvrbovsk@redhat.com>
This commit is contained in:
Rado Vrbovsky 2024-10-25 16:41:26 +00:00
commit 3a7781f68f
16 changed files with 436 additions and 95 deletions

View File

@ -47,6 +47,14 @@ Description:
disabled when the feature is used. See
Documentation/livepatch/livepatch.rst for more information.
What: /sys/kernel/livepatch/<patch>/replace
Date: Jun 2024
KernelVersion: 6.11.0
Contact: live-patching@vger.kernel.org
Description:
An attribute which indicates whether the patch supports
atomic-replace.
What: /sys/kernel/livepatch/<patch>/<object>
Date: Nov 2014
KernelVersion: 3.19.0

View File

@ -229,6 +229,10 @@ Contributing new tests (details)
TEST_PROGS, TEST_GEN_PROGS mean it is the executable tested by
default.
TEST_GEN_MODS_DIR should be used by tests that require modules to be built
before the test starts. The variable will contain the name of the directory
containing the modules.
TEST_CUSTOM_PROGS should be used by tests that require custom build
rules and prevent common build rule use.

View File

@ -20,9 +20,9 @@
#include <asm/livepatch.h>
/* task patch states */
#define KLP_UNDEFINED -1
#define KLP_UNPATCHED 0
#define KLP_PATCHED 1
#define KLP_TRANSITION_IDLE -1
#define KLP_TRANSITION_UNPATCHED 0
#define KLP_TRANSITION_PATCHED 1
/**
* struct klp_func - function structure for live patching

View File

@ -203,7 +203,7 @@ struct task_struct init_task
.trace_recursion = 0,
#endif
#ifdef CONFIG_LIVEPATCH
.patch_state = KLP_UNDEFINED,
.patch_state = KLP_TRANSITION_IDLE,
#endif
#ifdef CONFIG_SECURITY
.security = NULL,

View File

@ -339,6 +339,7 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
* /sys/kernel/livepatch/<patch>/enabled
* /sys/kernel/livepatch/<patch>/transition
* /sys/kernel/livepatch/<patch>/force
* /sys/kernel/livepatch/<patch>/replace
* /sys/kernel/livepatch/<patch>/<object>
* /sys/kernel/livepatch/<patch>/<object>/patched
* /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
@ -394,7 +395,7 @@ static ssize_t enabled_show(struct kobject *kobj,
struct klp_patch *patch;
patch = container_of(kobj, struct klp_patch, kobj);
return snprintf(buf, PAGE_SIZE-1, "%d\n", patch->enabled);
return sysfs_emit(buf, "%d\n", patch->enabled);
}
static ssize_t transition_show(struct kobject *kobj,
@ -403,8 +404,7 @@ static ssize_t transition_show(struct kobject *kobj,
struct klp_patch *patch;
patch = container_of(kobj, struct klp_patch, kobj);
return snprintf(buf, PAGE_SIZE-1, "%d\n",
patch == klp_transition_patch);
return sysfs_emit(buf, "%d\n", patch == klp_transition_patch);
}
static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr,
@ -436,13 +436,24 @@ static ssize_t force_store(struct kobject *kobj, struct kobj_attribute *attr,
return count;
}
static ssize_t replace_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
struct klp_patch *patch;
patch = container_of(kobj, struct klp_patch, kobj);
return sysfs_emit(buf, "%d\n", patch->replace);
}
static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled);
static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition);
static struct kobj_attribute force_kobj_attr = __ATTR_WO(force);
static struct kobj_attribute replace_kobj_attr = __ATTR_RO(replace);
static struct attribute *klp_patch_attrs[] = {
&enabled_kobj_attr.attr,
&transition_kobj_attr.attr,
&force_kobj_attr.attr,
&replace_kobj_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(klp_patch);
@ -966,7 +977,7 @@ static int __klp_disable_patch(struct klp_patch *patch)
if (klp_transition_patch)
return -EBUSY;
klp_init_transition(patch, KLP_UNPATCHED);
klp_init_transition(patch, KLP_TRANSITION_UNPATCHED);
klp_for_each_object(patch, obj)
if (obj->patched)
@ -1001,7 +1012,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
pr_notice("enabling patch '%s'\n", patch->mod->name);
klp_init_transition(patch, KLP_PATCHED);
klp_init_transition(patch, KLP_TRANSITION_PATCHED);
/*
* Enforce the order of the func->transition writes in

View File

@ -95,9 +95,9 @@ static void notrace klp_ftrace_handler(unsigned long ip,
patch_state = current->patch_state;
WARN_ON_ONCE(patch_state == KLP_UNDEFINED);
WARN_ON_ONCE(patch_state == KLP_TRANSITION_IDLE);
if (patch_state == KLP_UNPATCHED) {
if (patch_state == KLP_TRANSITION_UNPATCHED) {
/*
* Use the previously patched version of the function.
* If no previous patches exist, continue with the

View File

@ -24,7 +24,7 @@ static DEFINE_PER_CPU(unsigned long[MAX_STACK_ENTRIES], klp_stack_entries);
struct klp_patch *klp_transition_patch;
static int klp_target_state = KLP_UNDEFINED;
static int klp_target_state = KLP_TRANSITION_IDLE;
static unsigned int klp_signals_cnt;
@ -97,16 +97,16 @@ static void klp_complete_transition(void)
pr_debug("'%s': completing %s transition\n",
klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
klp_target_state == KLP_TRANSITION_PATCHED ? "patching" : "unpatching");
if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
if (klp_transition_patch->replace && klp_target_state == KLP_TRANSITION_PATCHED) {
klp_unpatch_replaced_patches(klp_transition_patch);
klp_discard_nops(klp_transition_patch);
}
if (klp_target_state == KLP_UNPATCHED) {
if (klp_target_state == KLP_TRANSITION_UNPATCHED) {
/*
* All tasks have transitioned to KLP_UNPATCHED so we can now
* All tasks have transitioned to KLP_TRANSITION_UNPATCHED so we can now
* remove the new functions from the func_stack.
*/
klp_unpatch_objects(klp_transition_patch);
@ -124,36 +124,36 @@ static void klp_complete_transition(void)
klp_for_each_func(obj, func)
func->transition = false;
/* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */
if (klp_target_state == KLP_PATCHED)
/* Prevent klp_ftrace_handler() from seeing KLP_TRANSITION_IDLE state */
if (klp_target_state == KLP_TRANSITION_PATCHED)
klp_synchronize_transition();
read_lock(&tasklist_lock);
for_each_process_thread(g, task) {
WARN_ON_ONCE(test_tsk_thread_flag(task, TIF_PATCH_PENDING));
task->patch_state = KLP_UNDEFINED;
task->patch_state = KLP_TRANSITION_IDLE;
}
read_unlock(&tasklist_lock);
for_each_possible_cpu(cpu) {
task = idle_task(cpu);
WARN_ON_ONCE(test_tsk_thread_flag(task, TIF_PATCH_PENDING));
task->patch_state = KLP_UNDEFINED;
task->patch_state = KLP_TRANSITION_IDLE;
}
klp_for_each_object(klp_transition_patch, obj) {
if (!klp_is_object_loaded(obj))
continue;
if (klp_target_state == KLP_PATCHED)
if (klp_target_state == KLP_TRANSITION_PATCHED)
klp_post_patch_callback(obj);
else if (klp_target_state == KLP_UNPATCHED)
else if (klp_target_state == KLP_TRANSITION_UNPATCHED)
klp_post_unpatch_callback(obj);
}
pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
klp_target_state == KLP_TRANSITION_PATCHED ? "patching" : "unpatching");
klp_target_state = KLP_UNDEFINED;
klp_target_state = KLP_TRANSITION_IDLE;
klp_transition_patch = NULL;
}
@ -165,13 +165,13 @@ static void klp_complete_transition(void)
*/
void klp_cancel_transition(void)
{
if (WARN_ON_ONCE(klp_target_state != KLP_PATCHED))
if (WARN_ON_ONCE(klp_target_state != KLP_TRANSITION_PATCHED))
return;
pr_debug("'%s': canceling patching transition, going to unpatch\n",
klp_transition_patch->mod->name);
klp_target_state = KLP_UNPATCHED;
klp_target_state = KLP_TRANSITION_UNPATCHED;
klp_complete_transition();
}
@ -219,7 +219,7 @@ static int klp_check_stack_func(struct klp_func *func, unsigned long *entries,
struct klp_ops *ops;
int i;
if (klp_target_state == KLP_UNPATCHED) {
if (klp_target_state == KLP_TRANSITION_UNPATCHED) {
/*
* Check for the to-be-unpatched function
* (the func itself).
@ -456,7 +456,7 @@ void klp_try_complete_transition(void)
struct klp_patch *patch;
bool complete = true;
WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);
WARN_ON_ONCE(klp_target_state == KLP_TRANSITION_IDLE);
/*
* Try to switch the tasks to the target patch state by walking their
@ -533,11 +533,11 @@ void klp_start_transition(void)
struct task_struct *g, *task;
unsigned int cpu;
WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);
WARN_ON_ONCE(klp_target_state == KLP_TRANSITION_IDLE);
pr_notice("'%s': starting %s transition\n",
klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
klp_target_state == KLP_TRANSITION_PATCHED ? "patching" : "unpatching");
/*
* Mark all normal tasks as needing a patch state update. They'll
@ -579,7 +579,7 @@ void klp_init_transition(struct klp_patch *patch, int state)
struct klp_func *func;
int initial_state = !state;
WARN_ON_ONCE(klp_target_state != KLP_UNDEFINED);
WARN_ON_ONCE(klp_target_state != KLP_TRANSITION_IDLE);
klp_transition_patch = patch;
@ -590,7 +590,7 @@ void klp_init_transition(struct klp_patch *patch, int state)
klp_target_state = state;
pr_debug("'%s': initializing %s transition\n", patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
klp_target_state == KLP_TRANSITION_PATCHED ? "patching" : "unpatching");
/*
* Initialize all tasks to the initial patch state to prepare them for
@ -598,7 +598,7 @@ void klp_init_transition(struct klp_patch *patch, int state)
*/
read_lock(&tasklist_lock);
for_each_process_thread(g, task) {
WARN_ON_ONCE(task->patch_state != KLP_UNDEFINED);
WARN_ON_ONCE(task->patch_state != KLP_TRANSITION_IDLE);
task->patch_state = initial_state;
}
read_unlock(&tasklist_lock);
@ -608,19 +608,19 @@ void klp_init_transition(struct klp_patch *patch, int state)
*/
for_each_possible_cpu(cpu) {
task = idle_task(cpu);
WARN_ON_ONCE(task->patch_state != KLP_UNDEFINED);
WARN_ON_ONCE(task->patch_state != KLP_TRANSITION_IDLE);
task->patch_state = initial_state;
}
/*
* Enforce the order of the task->patch_state initializations and the
* func->transition updates to ensure that klp_ftrace_handler() doesn't
* see a func in transition with a task->patch_state of KLP_UNDEFINED.
* see a func in transition with a task->patch_state of KLP_TRANSITION_IDLE.
*
* Also enforce the order of the klp_target_state write and future
* TIF_PATCH_PENDING writes to ensure klp_update_patch_state() and
* __klp_sched_try_switch() don't set a task->patch_state to
* KLP_UNDEFINED.
* KLP_TRANSITION_IDLE.
*/
smp_wmb();
@ -653,7 +653,7 @@ void klp_reverse_transition(void)
pr_debug("'%s': reversing transition from %s\n",
klp_transition_patch->mod->name,
klp_target_state == KLP_PATCHED ? "patching to unpatching" :
klp_target_state == KLP_TRANSITION_PATCHED ? "patching to unpatching" :
"unpatching to patching");
/*
@ -742,7 +742,7 @@ void klp_force_transition(void)
klp_update_patch_state(idle_task(cpu));
/* Set forced flag for patches being removed. */
if (klp_target_state == KLP_UNPATCHED)
if (klp_target_state == KLP_TRANSITION_UNPATCHED)
klp_transition_patch->forced = true;
else if (klp_transition_patch->replace) {
klp_for_each_patch(patch) {

View File

@ -8,7 +8,8 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
test_klp_callbacks_busy.o \
test_klp_callbacks_mod.o \
test_klp_livepatch.o \
test_klp_shadow_vars.o \
test_klp_state.o \
test_klp_state2.o \
test_klp_state3.o
test_klp_state3.o \
test_klp_shadow_vars.o \
test_klp_syscall.o

View File

@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017-2023 SUSE
* Authors: Libor Pechacek <lpechacek@suse.cz>
* Nicolai Stange <nstange@suse.de>
* Marcos Paulo de Souza <mpdesouza@suse.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/livepatch.h>
#if defined(__x86_64__)
#define FN_PREFIX __x64_
#elif defined(__s390x__)
#define FN_PREFIX __s390x_
#elif defined(__aarch64__)
#define FN_PREFIX __arm64_
#else
/* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER */
#define FN_PREFIX
#endif
/* Protects klp_pids */
static DEFINE_MUTEX(kpid_mutex);
static unsigned int npids, npids_pending;
static int klp_pids[NR_CPUS];
module_param_array(klp_pids, int, &npids_pending, 0);
MODULE_PARM_DESC(klp_pids, "Array of pids to be transitioned to livepatched state.");
static ssize_t npids_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%u\n", npids_pending);
}
static struct kobj_attribute klp_attr = __ATTR_RO(npids);
static struct kobject *klp_kobj;
static asmlinkage long lp_sys_getpid(void)
{
int i;
mutex_lock(&kpid_mutex);
if (npids_pending > 0) {
for (i = 0; i < npids; i++) {
if (current->pid == klp_pids[i]) {
klp_pids[i] = 0;
npids_pending--;
break;
}
}
}
mutex_unlock(&kpid_mutex);
return task_tgid_vnr(current);
}
static struct klp_func vmlinux_funcs[] = {
{
.old_name = __stringify(FN_PREFIX) "sys_getpid",
.new_func = lp_sys_getpid,
}, {}
};
static struct klp_object objs[] = {
{
/* name being NULL means vmlinux */
.funcs = vmlinux_funcs,
}, {}
};
static struct klp_patch patch = {
.mod = THIS_MODULE,
.objs = objs,
};
static int livepatch_init(void)
{
int ret;
klp_kobj = kobject_create_and_add("test_klp_syscall", kernel_kobj);
if (!klp_kobj)
return -ENOMEM;
ret = sysfs_create_file(klp_kobj, &klp_attr.attr);
if (ret) {
kobject_put(klp_kobj);
return ret;
}
/*
* Save the number pids to transition to livepatched state before the
* number of pending pids is decremented.
*/
npids = npids_pending;
return klp_enable_patch(&patch);
}
static void livepatch_exit(void)
{
kobject_put(klp_kobj);
}
module_init(livepatch_init);
module_exit(livepatch_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");
MODULE_AUTHOR("Libor Pechacek <lpechacek@suse.cz>");
MODULE_AUTHOR("Nicolai Stange <nstange@suse.de>");
MODULE_AUTHOR("Marcos Paulo de Souza <mpdesouza@suse.com>");
MODULE_DESCRIPTION("Livepatch test: syscall transition");

View File

@ -34,7 +34,8 @@ TEST_GEN_PROGS := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS))
TEST_GEN_PROGS_EXTENDED := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS_EXTENDED))
TEST_GEN_FILES := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_FILES))
all: $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES)
all: $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) \
$(if $(TEST_GEN_MODS_DIR),gen_mods_dir)
define RUN_TESTS
BASE_DIR="$(selfdir)"; \
@ -64,8 +65,8 @@ endef
run_tests: all
ifdef building_out_of_srctree
@if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then \
rsync -aLq $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT); \
@if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)$(TEST_GEN_MODS_DIR)" != "X" ]; then \
rsync -aLq $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(TEST_GEN_MODS_DIR) $(OUTPUT); \
fi
@$(INSTALL_INCLUDES)
@if [ "X$(TEST_PROGS)" != "X" ]; then \
@ -78,11 +79,22 @@ else
@$(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_PROGS))
endif
gen_mods_dir:
$(Q)$(MAKE) -C $(TEST_GEN_MODS_DIR)
clean_mods_dir:
$(Q)$(MAKE) -C $(TEST_GEN_MODS_DIR) clean
define INSTALL_SINGLE_RULE
$(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH))
$(if $(INSTALL_LIST),rsync -aL $(INSTALL_LIST) $(INSTALL_PATH)/)
endef
define INSTALL_MODS_RULE
$(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH)/$(INSTALL_LIST))
$(if $(INSTALL_LIST),rsync -a --copy-unsafe-links $(INSTALL_LIST)/*.ko $(INSTALL_PATH)/$(INSTALL_LIST))
endef
define INSTALL_RULE
$(eval INSTALL_LIST = $(TEST_PROGS)) $(INSTALL_SINGLE_RULE)
$(eval INSTALL_LIST = $(TEST_PROGS_EXTENDED)) $(INSTALL_SINGLE_RULE)
@ -91,6 +103,7 @@ define INSTALL_RULE
$(eval INSTALL_LIST = $(TEST_CUSTOM_PROGS)) $(INSTALL_SINGLE_RULE)
$(eval INSTALL_LIST = $(TEST_GEN_PROGS_EXTENDED)) $(INSTALL_SINGLE_RULE)
$(eval INSTALL_LIST = $(TEST_GEN_FILES)) $(INSTALL_SINGLE_RULE)
$(eval INSTALL_LIST = $(notdir $(TEST_GEN_MODS_DIR))) $(INSTALL_MODS_RULE)
$(eval INSTALL_LIST = $(wildcard config settings)) $(INSTALL_SINGLE_RULE)
endef
@ -117,7 +130,7 @@ define CLEAN
$(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN)
endef
clean:
clean: $(if $(TEST_GEN_MODS_DIR),clean_mods_dir)
$(CLEAN)
# When make O= with kselftest target from main level
@ -143,4 +156,4 @@ $(OUTPUT)/%:%.S
$(LINK.S) $^ $(LDLIBS) -o $@
endif
.PHONY: run_tests all clean install emit_tests
.PHONY: run_tests all clean install emit_tests gen_mods_dir clean_mods_dir

View File

@ -0,0 +1 @@
test_klp-call_getpid

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
TEST_GEN_FILES := test_klp-call_getpid
TEST_PROGS_EXTENDED := functions.sh
TEST_PROGS := \
test-livepatch.sh \
@ -7,7 +8,8 @@ TEST_PROGS := \
test-shadow-vars.sh \
test-state.sh \
test-ftrace.sh \
test-sysfs.sh
test-sysfs.sh \
test-syscall.sh
TEST_FILES := settings

View File

@ -4,7 +4,9 @@
. $(dirname $0)/functions.sh
MOD_LIVEPATCH=test_klp_livepatch
MOD_LIVEPATCH1=test_klp_livepatch
MOD_LIVEPATCH2=test_klp_syscall
MOD_LIVEPATCH3=test_klp_callbacks_demo
MOD_REPLACE=test_klp_atomic_replace
setup_config
@ -16,33 +18,33 @@ setup_config
start_test "basic function patching"
load_lp $MOD_LIVEPATCH
load_lp $MOD_LIVEPATCH1
if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then
if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH1: this has been live patched" ]] ; then
echo -e "FAIL\n\n"
die "livepatch kselftest(s) failed"
fi
disable_lp $MOD_LIVEPATCH
unload_lp $MOD_LIVEPATCH
disable_lp $MOD_LIVEPATCH1
unload_lp $MOD_LIVEPATCH1
if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH: this has been live patched" ]] ; then
if [[ "$(cat /proc/cmdline)" == "$MOD_LIVEPATCH1: this has been live patched" ]] ; then
echo -e "FAIL\n\n"
die "livepatch kselftest(s) failed"
fi
check_result "% modprobe $MOD_LIVEPATCH
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 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"
check_result "% modprobe $MOD_LIVEPATCH1
livepatch: enabling patch '$MOD_LIVEPATCH1'
livepatch: '$MOD_LIVEPATCH1': initializing patching transition
livepatch: '$MOD_LIVEPATCH1': starting patching transition
livepatch: '$MOD_LIVEPATCH1': completing patching transition
livepatch: '$MOD_LIVEPATCH1': patching complete
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH1/enabled
livepatch: '$MOD_LIVEPATCH1': initializing unpatching transition
livepatch: '$MOD_LIVEPATCH1': starting unpatching transition
livepatch: '$MOD_LIVEPATCH1': completing unpatching transition
livepatch: '$MOD_LIVEPATCH1': unpatching complete
% rmmod $MOD_LIVEPATCH1"
# - load a livepatch that modifies the output from /proc/cmdline and
@ -53,7 +55,7 @@ livepatch: '$MOD_LIVEPATCH': unpatching complete
start_test "multiple livepatches"
load_lp $MOD_LIVEPATCH
load_lp $MOD_LIVEPATCH1
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
@ -69,26 +71,26 @@ unload_lp $MOD_REPLACE
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
disable_lp $MOD_LIVEPATCH
unload_lp $MOD_LIVEPATCH
disable_lp $MOD_LIVEPATCH1
unload_lp $MOD_LIVEPATCH1
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
check_result "% modprobe $MOD_LIVEPATCH
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
$MOD_LIVEPATCH: this has been live patched
check_result "% modprobe $MOD_LIVEPATCH1
livepatch: enabling patch '$MOD_LIVEPATCH1'
livepatch: '$MOD_LIVEPATCH1': initializing patching transition
livepatch: '$MOD_LIVEPATCH1': starting patching transition
livepatch: '$MOD_LIVEPATCH1': completing patching transition
livepatch: '$MOD_LIVEPATCH1': patching complete
$MOD_LIVEPATCH1: this has been live patched
% modprobe $MOD_REPLACE replace=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
$MOD_LIVEPATCH: this has been live patched
$MOD_LIVEPATCH1: this has been live patched
$MOD_REPLACE: this has been live patched
% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled
livepatch: '$MOD_REPLACE': initializing unpatching transition
@ -96,35 +98,54 @@ livepatch: '$MOD_REPLACE': starting unpatching transition
livepatch: '$MOD_REPLACE': completing unpatching transition
livepatch: '$MOD_REPLACE': unpatching complete
% rmmod $MOD_REPLACE
$MOD_LIVEPATCH: this has been live patched
% 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"
$MOD_LIVEPATCH1: this has been live patched
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH1/enabled
livepatch: '$MOD_LIVEPATCH1': initializing unpatching transition
livepatch: '$MOD_LIVEPATCH1': starting unpatching transition
livepatch: '$MOD_LIVEPATCH1': completing unpatching transition
livepatch: '$MOD_LIVEPATCH1': unpatching complete
% rmmod $MOD_LIVEPATCH1"
# - load a livepatch that modifies the output from /proc/cmdline and
# verify correct behavior
# - load an atomic replace livepatch and verify that only the second is active
# - remove the first livepatch and verify that the atomic replace livepatch
# is still active
# - load two additional livepatches and check the number of livepatch modules
# applied
# - load an atomic replace livepatch and check that the other three modules were
# disabled
# - remove all livepatches besides the atomic replace one and verify that the
# atomic replace livepatch is still active
# - remove the atomic replace livepatch and verify that none are active
start_test "atomic replace livepatch"
load_lp $MOD_LIVEPATCH
load_lp $MOD_LIVEPATCH1
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
for mod in $MOD_LIVEPATCH2 $MOD_LIVEPATCH3; do
load_lp "$mod"
done
mods=(/sys/kernel/livepatch/*)
nmods=${#mods[@]}
if [ "$nmods" -ne 3 ]; then
die "Expecting three modules listed, found $nmods"
fi
load_lp $MOD_REPLACE replace=1
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
unload_lp $MOD_LIVEPATCH
loop_until 'mods=(/sys/kernel/livepatch/*); nmods=${#mods[@]}; [[ "$nmods" -eq 1 ]]' ||
die "Expecting only one moduled listed, found $nmods"
# These modules were disabled by the atomic replace
for mod in $MOD_LIVEPATCH3 $MOD_LIVEPATCH2 $MOD_LIVEPATCH1; do
unload_lp "$mod"
done
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
@ -135,13 +156,27 @@ unload_lp $MOD_REPLACE
grep 'live patched' /proc/cmdline > /dev/kmsg
grep 'live patched' /proc/meminfo > /dev/kmsg
check_result "% modprobe $MOD_LIVEPATCH
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
$MOD_LIVEPATCH: this has been live patched
check_result "% modprobe $MOD_LIVEPATCH1
livepatch: enabling patch '$MOD_LIVEPATCH1'
livepatch: '$MOD_LIVEPATCH1': initializing patching transition
livepatch: '$MOD_LIVEPATCH1': starting patching transition
livepatch: '$MOD_LIVEPATCH1': completing patching transition
livepatch: '$MOD_LIVEPATCH1': patching complete
$MOD_LIVEPATCH1: this has been live patched
% modprobe $MOD_LIVEPATCH2
livepatch: enabling patch '$MOD_LIVEPATCH2'
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
livepatch: '$MOD_LIVEPATCH2': starting patching transition
livepatch: '$MOD_LIVEPATCH2': completing patching transition
livepatch: '$MOD_LIVEPATCH2': patching complete
% modprobe $MOD_LIVEPATCH3
livepatch: enabling patch '$MOD_LIVEPATCH3'
livepatch: '$MOD_LIVEPATCH3': initializing patching transition
$MOD_LIVEPATCH3: pre_patch_callback: vmlinux
livepatch: '$MOD_LIVEPATCH3': starting patching transition
livepatch: '$MOD_LIVEPATCH3': completing patching transition
$MOD_LIVEPATCH3: post_patch_callback: vmlinux
livepatch: '$MOD_LIVEPATCH3': patching complete
% modprobe $MOD_REPLACE replace=1
livepatch: enabling patch '$MOD_REPLACE'
livepatch: '$MOD_REPLACE': initializing patching transition
@ -149,7 +184,9 @@ livepatch: '$MOD_REPLACE': starting patching transition
livepatch: '$MOD_REPLACE': completing patching transition
livepatch: '$MOD_REPLACE': patching complete
$MOD_REPLACE: this has been live patched
% rmmod $MOD_LIVEPATCH
% rmmod $MOD_LIVEPATCH3
% rmmod $MOD_LIVEPATCH2
% rmmod $MOD_LIVEPATCH1
$MOD_REPLACE: this has been live patched
% echo 0 > /sys/kernel/livepatch/$MOD_REPLACE/enabled
livepatch: '$MOD_REPLACE': initializing unpatching transition

View File

@ -0,0 +1,56 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2023 SUSE
# Author: Marcos Paulo de Souza <mpdesouza@suse.com>
. $(dirname $0)/functions.sh
MOD_SYSCALL=test_klp_syscall
setup_config
# - Start _NRPROC processes calling getpid and load a livepatch to patch the
# getpid syscall. Check if all the processes transitioned to the livepatched
# state.
start_test "patch getpid syscall while being heavily hammered"
NPROC=$(getconf _NPROCESSORS_ONLN)
MAXPROC=128
for i in $(seq 1 $(($NPROC < $MAXPROC ? $NPROC : $MAXPROC))); do
./test_klp-call_getpid &
pids[$i]="$!"
done
pid_list=$(echo ${pids[@]} | tr ' ' ',')
load_lp $MOD_SYSCALL klp_pids=$pid_list
# wait for all tasks to transition to patched state
loop_until 'grep -q '^0$' /sys/kernel/test_klp_syscall/npids'
pending_pids=$(cat /sys/kernel/test_klp_syscall/npids)
log "$MOD_SYSCALL: Remaining not livepatched processes: $pending_pids"
for pid in ${pids[@]}; do
kill $pid || true
done
disable_lp $MOD_SYSCALL
unload_lp $MOD_SYSCALL
check_result "% modprobe $MOD_SYSCALL klp_pids=$pid_list
livepatch: enabling patch '$MOD_SYSCALL'
livepatch: '$MOD_SYSCALL': initializing patching transition
livepatch: '$MOD_SYSCALL': starting patching transition
livepatch: '$MOD_SYSCALL': completing patching transition
livepatch: '$MOD_SYSCALL': patching complete
$MOD_SYSCALL: Remaining not livepatched processes: 0
% echo 0 > /sys/kernel/livepatch/$MOD_SYSCALL/enabled
livepatch: '$MOD_SYSCALL': initializing unpatching transition
livepatch: '$MOD_SYSCALL': starting unpatching transition
livepatch: '$MOD_SYSCALL': completing unpatching transition
livepatch: '$MOD_SYSCALL': unpatching complete
% rmmod $MOD_SYSCALL"
exit 0

View File

@ -18,6 +18,7 @@ check_sysfs_rights "$MOD_LIVEPATCH" "" "drwxr-xr-x"
check_sysfs_rights "$MOD_LIVEPATCH" "enabled" "-rw-r--r--"
check_sysfs_value "$MOD_LIVEPATCH" "enabled" "1"
check_sysfs_rights "$MOD_LIVEPATCH" "force" "--w-------"
check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--"
check_sysfs_rights "$MOD_LIVEPATCH" "transition" "-r--r--r--"
check_sysfs_value "$MOD_LIVEPATCH" "transition" "0"
check_sysfs_rights "$MOD_LIVEPATCH" "vmlinux/patched" "-r--r--r--"
@ -83,4 +84,51 @@ test_klp_callbacks_demo: post_unpatch_callback: vmlinux
livepatch: 'test_klp_callbacks_demo': unpatching complete
% rmmod test_klp_callbacks_demo"
start_test "sysfs test replace enabled"
MOD_LIVEPATCH=test_klp_atomic_replace
load_lp $MOD_LIVEPATCH replace=1
check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--"
check_sysfs_value "$MOD_LIVEPATCH" "replace" "1"
disable_lp $MOD_LIVEPATCH
unload_lp $MOD_LIVEPATCH
check_result "% modprobe $MOD_LIVEPATCH replace=1
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 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"
start_test "sysfs test replace disabled"
load_lp $MOD_LIVEPATCH replace=0
check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--"
check_sysfs_value "$MOD_LIVEPATCH" "replace" "0"
disable_lp $MOD_LIVEPATCH
unload_lp $MOD_LIVEPATCH
check_result "% modprobe $MOD_LIVEPATCH replace=0
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 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"
exit 0

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 SUSE
* Authors: Libor Pechacek <lpechacek@suse.cz>
* Marcos Paulo de Souza <mpdesouza@suse.com>
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>
static int stop;
static int sig_int;
void hup_handler(int signum)
{
stop = 1;
}
void int_handler(int signum)
{
stop = 1;
sig_int = 1;
}
int main(int argc, char *argv[])
{
long count = 0;
signal(SIGHUP, &hup_handler);
signal(SIGINT, &int_handler);
while (!stop) {
(void)syscall(SYS_getpid);
count++;
}
if (sig_int)
printf("%ld iterations done\n", count);
return 0;
}