Skip to content

Commit e2a8c62

Browse files
committed
Merge: livepatch: selected fixes for rhel-9.7 v2
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/6654 JIRA: https://issues.redhat.com/browse/RHEL-85303 A small series of fixes for the RHEL9.7 livepatch subsystem. Signed-off-by: Denis Aleksandrov <[email protected]> Approved-by: Joe Lawrence <[email protected]> Approved-by: Ryan Sullivan <[email protected]> Approved-by: CKI KWF Bot <[email protected]> Merged-by: Augusto Caringi <[email protected]>
2 parents 63ccd7e + 15dc6dd commit e2a8c62

File tree

8 files changed

+135
-19
lines changed

8 files changed

+135
-19
lines changed

Documentation/ABI/testing/sysfs-kernel-livepatch

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ Description:
5555
An attribute which indicates whether the patch supports
5656
atomic-replace.
5757

58+
What: /sys/kernel/livepatch/<patch>/stack_order
59+
Date: Jan 2025
60+
KernelVersion: 6.14.0
61+
Description:
62+
This attribute specifies the sequence in which live patch modules
63+
are applied to the system. If multiple live patches modify the same
64+
function, the implementation with the biggest 'stack_order' number
65+
is used, unless a transition is currently in progress.
66+
5867
What: /sys/kernel/livepatch/<patch>/<object>
5968
Date: Nov 2014
6069
KernelVersion: 3.19.0

kernel/livepatch/core.c

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,24 @@ static int klp_find_callback(void *data, const char *name, unsigned long addr)
146146
return 0;
147147
}
148148

149+
static int klp_match_callback(void *data, unsigned long addr)
150+
{
151+
struct klp_find_arg *args = data;
152+
153+
args->addr = addr;
154+
args->count++;
155+
156+
/*
157+
* Finish the search when the symbol is found for the desired position
158+
* or the position is not defined for a non-unique symbol.
159+
*/
160+
if ((args->pos && (args->count == args->pos)) ||
161+
(!args->pos && (args->count > 1)))
162+
return 1;
163+
164+
return 0;
165+
}
166+
149167
static int klp_find_object_symbol(const char *objname, const char *name,
150168
unsigned long sympos, unsigned long *addr)
151169
{
@@ -159,7 +177,7 @@ static int klp_find_object_symbol(const char *objname, const char *name,
159177
if (objname)
160178
module_kallsyms_on_each_symbol(objname, klp_find_callback, &args);
161179
else
162-
kallsyms_on_each_symbol(klp_find_callback, &args);
180+
kallsyms_on_each_match_symbol(klp_match_callback, name, &args);
163181

164182
/*
165183
* Ensure an address was found. If sympos is 0, ensure symbol is unique;
@@ -182,7 +200,7 @@ static int klp_find_object_symbol(const char *objname, const char *name,
182200
return -EINVAL;
183201
}
184202

185-
static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
203+
static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,
186204
unsigned int symndx, Elf_Shdr *relasec,
187205
const char *sec_objname)
188206
{
@@ -210,7 +228,7 @@ static int klp_resolve_symbols(Elf64_Shdr *sechdrs, const char *strtab,
210228
relas = (Elf_Rela *) relasec->sh_addr;
211229
/* For each rela in this klp relocation section */
212230
for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) {
213-
sym = (Elf64_Sym *)sechdrs[symndx].sh_addr + ELF_R_SYM(relas[i].r_info);
231+
sym = (Elf_Sym *)sechdrs[symndx].sh_addr + ELF_R_SYM(relas[i].r_info);
214232
if (sym->st_shndx != SHN_LIVEPATCH) {
215233
pr_err("symbol %s is not marked as a livepatch symbol\n",
216234
strtab + sym->st_name);
@@ -340,6 +358,7 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,
340358
* /sys/kernel/livepatch/<patch>/transition
341359
* /sys/kernel/livepatch/<patch>/force
342360
* /sys/kernel/livepatch/<patch>/replace
361+
* /sys/kernel/livepatch/<patch>/stack_order
343362
* /sys/kernel/livepatch/<patch>/<object>
344363
* /sys/kernel/livepatch/<patch>/<object>/patched
345364
* /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
@@ -445,15 +464,38 @@ static ssize_t replace_show(struct kobject *kobj,
445464
return sysfs_emit(buf, "%d\n", patch->replace);
446465
}
447466

467+
static ssize_t stack_order_show(struct kobject *kobj,
468+
struct kobj_attribute *attr, char *buf)
469+
{
470+
struct klp_patch *patch, *this_patch;
471+
int stack_order = 0;
472+
473+
this_patch = container_of(kobj, struct klp_patch, kobj);
474+
475+
mutex_lock(&klp_mutex);
476+
477+
klp_for_each_patch(patch) {
478+
stack_order++;
479+
if (patch == this_patch)
480+
break;
481+
}
482+
483+
mutex_unlock(&klp_mutex);
484+
485+
return sysfs_emit(buf, "%d\n", stack_order);
486+
}
487+
448488
static struct kobj_attribute enabled_kobj_attr = __ATTR_RW(enabled);
449489
static struct kobj_attribute transition_kobj_attr = __ATTR_RO(transition);
450490
static struct kobj_attribute force_kobj_attr = __ATTR_WO(force);
451491
static struct kobj_attribute replace_kobj_attr = __ATTR_RO(replace);
492+
static struct kobj_attribute stack_order_kobj_attr = __ATTR_RO(stack_order);
452493
static struct attribute *klp_patch_attrs[] = {
453494
&enabled_kobj_attr.attr,
454495
&transition_kobj_attr.attr,
455496
&force_kobj_attr.attr,
456497
&replace_kobj_attr.attr,
498+
&stack_order_kobj_attr.attr,
457499
NULL
458500
};
459501
ATTRIBUTE_GROUPS(klp_patch);

samples/livepatch/livepatch-callbacks-busymod.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ static void busymod_work_func(struct work_struct *work)
4444
static int livepatch_callbacks_mod_init(void)
4545
{
4646
pr_info("%s\n", __func__);
47-
schedule_delayed_work(&work,
48-
msecs_to_jiffies(1000 * 0));
47+
schedule_delayed_work(&work, 0);
4948
return 0;
5049
}
5150

samples/livepatch/livepatch-shadow-fix1.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ static struct dummy *livepatch_fix1_dummy_alloc(void)
7272
if (!d)
7373
return NULL;
7474

75-
d->jiffies_expire = jiffies +
76-
msecs_to_jiffies(1000 * EXPIRE_PERIOD);
75+
d->jiffies_expire = jiffies + secs_to_jiffies(EXPIRE_PERIOD);
7776

7877
/*
7978
* Patch: save the extra memory location into a SV_LEAK shadow

samples/livepatch/livepatch-shadow-mod.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ static __used noinline struct dummy *dummy_alloc(void)
101101
if (!d)
102102
return NULL;
103103

104-
d->jiffies_expire = jiffies +
105-
msecs_to_jiffies(1000 * EXPIRE_PERIOD);
104+
d->jiffies_expire = jiffies + secs_to_jiffies(EXPIRE_PERIOD);
106105

107106
/* Oops, forgot to save leak! */
108107
leak = kzalloc(sizeof(*leak), GFP_KERNEL);
@@ -152,8 +151,7 @@ static void alloc_work_func(struct work_struct *work)
152151
list_add(&d->list, &dummy_list);
153152
mutex_unlock(&dummy_list_mutex);
154153

155-
schedule_delayed_work(&alloc_dwork,
156-
msecs_to_jiffies(1000 * ALLOC_PERIOD));
154+
schedule_delayed_work(&alloc_dwork, secs_to_jiffies(ALLOC_PERIOD));
157155
}
158156

159157
/*
@@ -184,16 +182,13 @@ static void cleanup_work_func(struct work_struct *work)
184182
}
185183
mutex_unlock(&dummy_list_mutex);
186184

187-
schedule_delayed_work(&cleanup_dwork,
188-
msecs_to_jiffies(1000 * CLEANUP_PERIOD));
185+
schedule_delayed_work(&cleanup_dwork, secs_to_jiffies(CLEANUP_PERIOD));
189186
}
190187

191188
static int livepatch_shadow_mod_init(void)
192189
{
193-
schedule_delayed_work(&alloc_dwork,
194-
msecs_to_jiffies(1000 * ALLOC_PERIOD));
195-
schedule_delayed_work(&cleanup_dwork,
196-
msecs_to_jiffies(1000 * CLEANUP_PERIOD));
190+
schedule_delayed_work(&alloc_dwork, secs_to_jiffies(ALLOC_PERIOD));
191+
schedule_delayed_work(&cleanup_dwork, secs_to_jiffies(CLEANUP_PERIOD));
197192

198193
return 0;
199194
}

tools/testing/selftests/livepatch/functions.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,8 @@ function check_result {
301301
result=$(dmesg | awk -v last_dmesg="$LAST_DMESG" 'p; $0 == last_dmesg { p=1 }' | \
302302
grep -e 'livepatch:' -e 'test_klp' | \
303303
grep -v '\(tainting\|taints\) kernel' | \
304-
sed 's/^\[[ 0-9.]*\] //')
304+
sed 's/^\[[ 0-9.]*\] //' | \
305+
sed 's/^\[[ ]*[CT][0-9]*\] //')
305306

306307
if [[ "$expect" == "$result" ]] ; then
307308
echo "ok"

tools/testing/selftests/livepatch/test-callbacks.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ $MOD_TARGET: ${MOD_TARGET}_init
259259
% modprobe $MOD_LIVEPATCH pre_patch_ret=-19
260260
livepatch: enabling patch '$MOD_LIVEPATCH'
261261
livepatch: '$MOD_LIVEPATCH': initializing patching transition
262-
test_klp_callbacks_demo: pre_patch_callback: vmlinux
262+
$MOD_LIVEPATCH: pre_patch_callback: vmlinux
263263
livepatch: pre-patch callback failed for object 'vmlinux'
264264
livepatch: failed to enable patch '$MOD_LIVEPATCH'
265265
livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch

tools/testing/selftests/livepatch/test-sysfs.sh

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
. $(dirname $0)/functions.sh
66

77
MOD_LIVEPATCH=test_klp_livepatch
8+
MOD_LIVEPATCH2=test_klp_callbacks_demo
9+
MOD_LIVEPATCH3=test_klp_syscall
810

911
setup_config
1012

@@ -19,6 +21,8 @@ check_sysfs_rights "$MOD_LIVEPATCH" "enabled" "-rw-r--r--"
1921
check_sysfs_value "$MOD_LIVEPATCH" "enabled" "1"
2022
check_sysfs_rights "$MOD_LIVEPATCH" "force" "--w-------"
2123
check_sysfs_rights "$MOD_LIVEPATCH" "replace" "-r--r--r--"
24+
check_sysfs_rights "$MOD_LIVEPATCH" "stack_order" "-r--r--r--"
25+
check_sysfs_value "$MOD_LIVEPATCH" "stack_order" "1"
2226
check_sysfs_rights "$MOD_LIVEPATCH" "transition" "-r--r--r--"
2327
check_sysfs_value "$MOD_LIVEPATCH" "transition" "0"
2428
check_sysfs_rights "$MOD_LIVEPATCH" "vmlinux/patched" "-r--r--r--"
@@ -131,4 +135,71 @@ livepatch: '$MOD_LIVEPATCH': completing unpatching transition
131135
livepatch: '$MOD_LIVEPATCH': unpatching complete
132136
% rmmod $MOD_LIVEPATCH"
133137

138+
start_test "sysfs test stack_order value"
139+
140+
load_lp $MOD_LIVEPATCH
141+
142+
check_sysfs_value "$MOD_LIVEPATCH" "stack_order" "1"
143+
144+
load_lp $MOD_LIVEPATCH2
145+
146+
check_sysfs_value "$MOD_LIVEPATCH2" "stack_order" "2"
147+
148+
load_lp $MOD_LIVEPATCH3
149+
150+
check_sysfs_value "$MOD_LIVEPATCH3" "stack_order" "3"
151+
152+
disable_lp $MOD_LIVEPATCH2
153+
unload_lp $MOD_LIVEPATCH2
154+
155+
check_sysfs_value "$MOD_LIVEPATCH" "stack_order" "1"
156+
check_sysfs_value "$MOD_LIVEPATCH3" "stack_order" "2"
157+
158+
disable_lp $MOD_LIVEPATCH3
159+
unload_lp $MOD_LIVEPATCH3
160+
161+
disable_lp $MOD_LIVEPATCH
162+
unload_lp $MOD_LIVEPATCH
163+
164+
check_result "% modprobe $MOD_LIVEPATCH
165+
livepatch: enabling patch '$MOD_LIVEPATCH'
166+
livepatch: '$MOD_LIVEPATCH': initializing patching transition
167+
livepatch: '$MOD_LIVEPATCH': starting patching transition
168+
livepatch: '$MOD_LIVEPATCH': completing patching transition
169+
livepatch: '$MOD_LIVEPATCH': patching complete
170+
% modprobe $MOD_LIVEPATCH2
171+
livepatch: enabling patch '$MOD_LIVEPATCH2'
172+
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
173+
$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
174+
livepatch: '$MOD_LIVEPATCH2': starting patching transition
175+
livepatch: '$MOD_LIVEPATCH2': completing patching transition
176+
$MOD_LIVEPATCH2: post_patch_callback: vmlinux
177+
livepatch: '$MOD_LIVEPATCH2': patching complete
178+
% modprobe $MOD_LIVEPATCH3
179+
livepatch: enabling patch '$MOD_LIVEPATCH3'
180+
livepatch: '$MOD_LIVEPATCH3': initializing patching transition
181+
livepatch: '$MOD_LIVEPATCH3': starting patching transition
182+
livepatch: '$MOD_LIVEPATCH3': completing patching transition
183+
livepatch: '$MOD_LIVEPATCH3': patching complete
184+
% echo 0 > $SYSFS_KLP_DIR/$MOD_LIVEPATCH2/enabled
185+
livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
186+
$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
187+
livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
188+
livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
189+
$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
190+
livepatch: '$MOD_LIVEPATCH2': unpatching complete
191+
% rmmod $MOD_LIVEPATCH2
192+
% echo 0 > $SYSFS_KLP_DIR/$MOD_LIVEPATCH3/enabled
193+
livepatch: '$MOD_LIVEPATCH3': initializing unpatching transition
194+
livepatch: '$MOD_LIVEPATCH3': starting unpatching transition
195+
livepatch: '$MOD_LIVEPATCH3': completing unpatching transition
196+
livepatch: '$MOD_LIVEPATCH3': unpatching complete
197+
% rmmod $MOD_LIVEPATCH3
198+
% echo 0 > $SYSFS_KLP_DIR/$MOD_LIVEPATCH/enabled
199+
livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
200+
livepatch: '$MOD_LIVEPATCH': starting unpatching transition
201+
livepatch: '$MOD_LIVEPATCH': completing unpatching transition
202+
livepatch: '$MOD_LIVEPATCH': unpatching complete
203+
% rmmod $MOD_LIVEPATCH"
204+
134205
exit 0

0 commit comments

Comments
 (0)