Skip to content

Commit 4de65c5

Browse files
committed
Merge tag 'livepatching-for-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching
Pull livepatching updates from Petr Mladek: - Fix race between fork and livepatch transition revert - Add sysfs entry that shows "patched" state for each object (module) that can be livepatched by the given livepatch - Some clean up * tag 'livepatching-for-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching: selftests/livepatch: add sysfs test livepatch: add sysfs entry "patched" for each klp_object selftests/livepatch: normalize sysctl error message livepatch: Add a missing newline character in klp_module_coming() livepatch: fix race between fork and KLP transition
2 parents b520410 + 59b2a38 commit 4de65c5

File tree

6 files changed

+166
-5
lines changed

6 files changed

+166
-5
lines changed

Documentation/ABI/testing/sysfs-kernel-livepatch

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ Description:
5555
The object directory contains subdirectories for each function
5656
that is patched within the object.
5757

58+
What: /sys/kernel/livepatch/<patch>/<object>/patched
59+
Date: August 2022
60+
KernelVersion: 6.1.0
61+
62+
Description:
63+
An attribute which indicates whether the object is currently
64+
patched.
65+
5866
What: /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
5967
Date: Nov 2014
6068
KernelVersion: 3.19.0

kernel/livepatch/core.c

+19-1
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
325325
* /sys/kernel/livepatch/<patch>/transition
326326
* /sys/kernel/livepatch/<patch>/force
327327
* /sys/kernel/livepatch/<patch>/<object>
328+
* /sys/kernel/livepatch/<patch>/<object>/patched
328329
* /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
329330
*/
330331
static int __klp_disable_patch(struct klp_patch *patch);
@@ -431,6 +432,22 @@ static struct attribute *klp_patch_attrs[] = {
431432
};
432433
ATTRIBUTE_GROUPS(klp_patch);
433434

435+
static ssize_t patched_show(struct kobject *kobj,
436+
struct kobj_attribute *attr, char *buf)
437+
{
438+
struct klp_object *obj;
439+
440+
obj = container_of(kobj, struct klp_object, kobj);
441+
return sysfs_emit(buf, "%d\n", obj->patched);
442+
}
443+
444+
static struct kobj_attribute patched_kobj_attr = __ATTR_RO(patched);
445+
static struct attribute *klp_object_attrs[] = {
446+
&patched_kobj_attr.attr,
447+
NULL,
448+
};
449+
ATTRIBUTE_GROUPS(klp_object);
450+
434451
static void klp_free_object_dynamic(struct klp_object *obj)
435452
{
436453
kfree(obj->name);
@@ -576,6 +593,7 @@ static void klp_kobj_release_object(struct kobject *kobj)
576593
static struct kobj_type klp_ktype_object = {
577594
.release = klp_kobj_release_object,
578595
.sysfs_ops = &kobj_sysfs_ops,
596+
.default_groups = klp_object_groups,
579597
};
580598

581599
static void klp_kobj_release_func(struct kobject *kobj)
@@ -1171,7 +1189,7 @@ int klp_module_coming(struct module *mod)
11711189
return -EINVAL;
11721190

11731191
if (!strcmp(mod->name, "vmlinux")) {
1174-
pr_err("vmlinux.ko: invalid module name");
1192+
pr_err("vmlinux.ko: invalid module name\n");
11751193
return -EINVAL;
11761194
}
11771195

kernel/livepatch/transition.c

+16-2
Original file line numberDiff line numberDiff line change
@@ -610,9 +610,23 @@ void klp_reverse_transition(void)
610610
/* Called from copy_process() during fork */
611611
void klp_copy_process(struct task_struct *child)
612612
{
613-
child->patch_state = current->patch_state;
614613

615-
/* TIF_PATCH_PENDING gets copied in setup_thread_stack() */
614+
/*
615+
* The parent process may have gone through a KLP transition since
616+
* the thread flag was copied in setup_thread_stack earlier. Bring
617+
* the task flag up to date with the parent here.
618+
*
619+
* The operation is serialized against all klp_*_transition()
620+
* operations by the tasklist_lock. The only exception is
621+
* klp_update_patch_state(current), but we cannot race with
622+
* that because we are current.
623+
*/
624+
if (test_tsk_thread_flag(current, TIF_PATCH_PENDING))
625+
set_tsk_thread_flag(child, TIF_PATCH_PENDING);
626+
else
627+
clear_tsk_thread_flag(child, TIF_PATCH_PENDING);
628+
629+
child->patch_state = current->patch_state;
616630
}
617631

618632
/*

tools/testing/selftests/livepatch/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ TEST_PROGS := \
66
test-callbacks.sh \
77
test-shadow-vars.sh \
88
test-state.sh \
9-
test-ftrace.sh
9+
test-ftrace.sh \
10+
test-sysfs.sh
1011

1112
TEST_FILES := settings
1213

tools/testing/selftests/livepatch/functions.sh

+35-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
MAX_RETRIES=600
88
RETRY_INTERVAL=".1" # seconds
9+
KLP_SYSFS_DIR="/sys/kernel/livepatch"
910

1011
# Kselftest framework requirement - SKIP code is 4
1112
ksft_skip=4
@@ -86,7 +87,7 @@ function set_ftrace_enabled() {
8687

8788
if [[ "$result" != "$1" ]] ; then
8889
if [[ $can_fail -eq 1 ]] ; then
89-
echo "livepatch: $err" > /dev/kmsg
90+
echo "livepatch: $err" | sed 's#/proc/sys/kernel/#kernel.#' > /dev/kmsg
9091
return
9192
fi
9293

@@ -308,3 +309,36 @@ function check_result {
308309

309310
cleanup_dmesg_file
310311
}
312+
313+
# check_sysfs_rights(modname, rel_path, expected_rights) - check sysfs
314+
# path permissions
315+
# modname - livepatch module creating the sysfs interface
316+
# rel_path - relative path of the sysfs interface
317+
# expected_rights - expected access rights
318+
function check_sysfs_rights() {
319+
local mod="$1"; shift
320+
local rel_path="$1"; shift
321+
local expected_rights="$1"; shift
322+
323+
local path="$KLP_SYSFS_DIR/$mod/$rel_path"
324+
local rights=$(/bin/stat --format '%A' "$path")
325+
if test "$rights" != "$expected_rights" ; then
326+
die "Unexpected access rights of $path: $expected_rights vs. $rights"
327+
fi
328+
}
329+
330+
# check_sysfs_value(modname, rel_path, expected_value) - check sysfs value
331+
# modname - livepatch module creating the sysfs interface
332+
# rel_path - relative path of the sysfs interface
333+
# expected_value - expected value read from the file
334+
function check_sysfs_value() {
335+
local mod="$1"; shift
336+
local rel_path="$1"; shift
337+
local expected_value="$1"; shift
338+
339+
local path="$KLP_SYSFS_DIR/$mod/$rel_path"
340+
local value=`cat $path`
341+
if test "$value" != "$expected_value" ; then
342+
die "Unexpected value in $path: $expected_value vs. $value"
343+
fi
344+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-2.0
3+
# Copyright (C) 2022 Song Liu <[email protected]>
4+
5+
. $(dirname $0)/functions.sh
6+
7+
MOD_LIVEPATCH=test_klp_livepatch
8+
9+
setup_config
10+
11+
# - load a livepatch and verifies the sysfs entries work as expected
12+
13+
start_test "sysfs test"
14+
15+
load_lp $MOD_LIVEPATCH
16+
17+
check_sysfs_rights "$MOD_LIVEPATCH" "" "drwxr-xr-x"
18+
check_sysfs_rights "$MOD_LIVEPATCH" "enabled" "-rw-r--r--"
19+
check_sysfs_value "$MOD_LIVEPATCH" "enabled" "1"
20+
check_sysfs_rights "$MOD_LIVEPATCH" "force" "--w-------"
21+
check_sysfs_rights "$MOD_LIVEPATCH" "transition" "-r--r--r--"
22+
check_sysfs_value "$MOD_LIVEPATCH" "transition" "0"
23+
check_sysfs_rights "$MOD_LIVEPATCH" "vmlinux/patched" "-r--r--r--"
24+
check_sysfs_value "$MOD_LIVEPATCH" "vmlinux/patched" "1"
25+
26+
disable_lp $MOD_LIVEPATCH
27+
28+
unload_lp $MOD_LIVEPATCH
29+
30+
check_result "% modprobe $MOD_LIVEPATCH
31+
livepatch: enabling patch '$MOD_LIVEPATCH'
32+
livepatch: '$MOD_LIVEPATCH': initializing patching transition
33+
livepatch: '$MOD_LIVEPATCH': starting patching transition
34+
livepatch: '$MOD_LIVEPATCH': completing patching transition
35+
livepatch: '$MOD_LIVEPATCH': patching complete
36+
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
37+
livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
38+
livepatch: '$MOD_LIVEPATCH': starting unpatching transition
39+
livepatch: '$MOD_LIVEPATCH': completing unpatching transition
40+
livepatch: '$MOD_LIVEPATCH': unpatching complete
41+
% rmmod $MOD_LIVEPATCH"
42+
43+
start_test "sysfs test object/patched"
44+
45+
MOD_LIVEPATCH=test_klp_callbacks_demo
46+
MOD_TARGET=test_klp_callbacks_mod
47+
load_lp $MOD_LIVEPATCH
48+
49+
# check the "patch" file changes as target module loads/unloads
50+
check_sysfs_value "$MOD_LIVEPATCH" "$MOD_TARGET/patched" "0"
51+
load_mod $MOD_TARGET
52+
check_sysfs_value "$MOD_LIVEPATCH" "$MOD_TARGET/patched" "1"
53+
unload_mod $MOD_TARGET
54+
check_sysfs_value "$MOD_LIVEPATCH" "$MOD_TARGET/patched" "0"
55+
56+
disable_lp $MOD_LIVEPATCH
57+
unload_lp $MOD_LIVEPATCH
58+
59+
check_result "% modprobe test_klp_callbacks_demo
60+
livepatch: enabling patch 'test_klp_callbacks_demo'
61+
livepatch: 'test_klp_callbacks_demo': initializing patching transition
62+
test_klp_callbacks_demo: pre_patch_callback: vmlinux
63+
livepatch: 'test_klp_callbacks_demo': starting patching transition
64+
livepatch: 'test_klp_callbacks_demo': completing patching transition
65+
test_klp_callbacks_demo: post_patch_callback: vmlinux
66+
livepatch: 'test_klp_callbacks_demo': patching complete
67+
% modprobe test_klp_callbacks_mod
68+
livepatch: applying patch 'test_klp_callbacks_demo' to loading module 'test_klp_callbacks_mod'
69+
test_klp_callbacks_demo: pre_patch_callback: test_klp_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
70+
test_klp_callbacks_demo: post_patch_callback: test_klp_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init
71+
test_klp_callbacks_mod: test_klp_callbacks_mod_init
72+
% rmmod test_klp_callbacks_mod
73+
test_klp_callbacks_mod: test_klp_callbacks_mod_exit
74+
test_klp_callbacks_demo: pre_unpatch_callback: test_klp_callbacks_mod -> [MODULE_STATE_GOING] Going away
75+
livepatch: reverting patch 'test_klp_callbacks_demo' on unloading module 'test_klp_callbacks_mod'
76+
test_klp_callbacks_demo: post_unpatch_callback: test_klp_callbacks_mod -> [MODULE_STATE_GOING] Going away
77+
% echo 0 > /sys/kernel/livepatch/test_klp_callbacks_demo/enabled
78+
livepatch: 'test_klp_callbacks_demo': initializing unpatching transition
79+
test_klp_callbacks_demo: pre_unpatch_callback: vmlinux
80+
livepatch: 'test_klp_callbacks_demo': starting unpatching transition
81+
livepatch: 'test_klp_callbacks_demo': completing unpatching transition
82+
test_klp_callbacks_demo: post_unpatch_callback: vmlinux
83+
livepatch: 'test_klp_callbacks_demo': unpatching complete
84+
% rmmod test_klp_callbacks_demo"
85+
86+
exit 0

0 commit comments

Comments
 (0)