* [PATCH v3 02/13] objtool/klp: fix mkstemp() failure with long paths
From: Joe Lawrence @ 2026-02-17 16:06 UTC (permalink / raw)
To: live-patching
Cc: Josh Poimboeuf, Song Liu, Jiri Kosina, Miroslav Benes,
Petr Mladek
In-Reply-To: <20260217160645.3434685-1-joe.lawrence@redhat.com>
The elf_create_file() function fails with EINVAL when the build directory
path is long enough to truncate the "XXXXXX" suffix in the 256-byte
tmp_name buffer.
Simplify the code to remove the unnecessary dirname()/basename() split
and concatenation. Instead, allocate the exact number of bytes needed for
the path.
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
tools/objtool/elf.c | 23 +++--------------------
1 file changed, 3 insertions(+), 20 deletions(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index bd6502e7bdc0..6f6d1c4cb6af 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -16,7 +16,6 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <libgen.h>
#include <ctype.h>
#include <linux/align.h>
#include <linux/kernel.h>
@@ -1189,7 +1188,7 @@ struct elf *elf_open_read(const char *name, int flags)
struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
{
struct section *null, *symtab, *strtab, *shstrtab;
- char *dir, *base, *tmp_name;
+ char *tmp_name;
struct symbol *sym;
struct elf *elf;
@@ -1203,29 +1202,13 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)
INIT_LIST_HEAD(&elf->sections);
- dir = strdup(name);
- if (!dir) {
- ERROR_GLIBC("strdup");
- return NULL;
- }
-
- dir = dirname(dir);
-
- base = strdup(name);
- if (!base) {
- ERROR_GLIBC("strdup");
- return NULL;
- }
-
- base = basename(base);
-
- tmp_name = malloc(256);
+ tmp_name = malloc(strlen(name) + 8);
if (!tmp_name) {
ERROR_GLIBC("malloc");
return NULL;
}
- snprintf(tmp_name, 256, "%s/%s.XXXXXX", dir, base);
+ sprintf(tmp_name, "%s.XXXXXX", name);
elf->fd = mkstemp(tmp_name);
if (elf->fd == -1) {
--
2.53.0
^ permalink raw reply related
* [PATCH v3 01/13] objtool/klp: honor SHF_MERGE entry alignment in elf_add_data()
From: Joe Lawrence @ 2026-02-17 16:06 UTC (permalink / raw)
To: live-patching
Cc: Josh Poimboeuf, Song Liu, Jiri Kosina, Miroslav Benes,
Petr Mladek
In-Reply-To: <20260217160645.3434685-1-joe.lawrence@redhat.com>
When adding data to an SHF_MERGE section, set the Elf_Data d_align to
the section's sh_addralign so libelf aligns entries within the section.
This ensures that entry offsets are consistent with previously calculated
relocation addends.
Fixes: 431dbabf2d9d ("objtool: Add elf_create_data()")
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
tools/objtool/elf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 2c02c7b49265..bd6502e7bdc0 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -1375,7 +1375,7 @@ void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_
memcpy(sec->data->d_buf, data, size);
sec->data->d_size = size;
- sec->data->d_align = 1;
+ sec->data->d_align = (sec->sh.sh_flags & SHF_MERGE) ? sec->sh.sh_addralign : 1;
offset = ALIGN(sec->sh.sh_size, sec->sh.sh_addralign);
sec->sh.sh_size = offset + size;
--
2.53.0
^ permalink raw reply related
* [PATCH v3 00/13] livepatch-klp-build: small fixups and enhancements
From: Joe Lawrence @ 2026-02-17 16:06 UTC (permalink / raw)
To: live-patching
Cc: Josh Poimboeuf, Song Liu, Jiri Kosina, Miroslav Benes,
Petr Mladek
Here is v3, addressing review feedback from the v2 thread as well as
finding a few more things along the way.
I'll reply to specific patches with more details on new bugs/behavior.
I've ordered the patchset IMHO order of importance, fixing bugs then
moving on to enhancements.
v3:
- Added a patch to objtool ELF code to fix packed string section alignment
- Simplified the mkstemp() patch and surrounding code [Josh]
- Added patches to catch grep use, add Makefile shellcheck, and fix
current shellcheck warnings [Josh]
- Simplified the short-circuit validation patch [Josh]
- Added a patch to fix short-circuit version mismatch
- Pretty-print output in color [Josh]
- Reduce 'patch' chatter to fuzz/offset warning during patch validation
- Prune tools/ from paths that find_objects() looks for changed objects
v2: https://lore.kernel.org/live-patching/20260204025140.2023382-1-joe.lawrence@redhat.com/
- Update patch subject prefixes accordingly [Josh]
- Added a small objtool/klp patch. Test systems setup crazy long
pathnames :D
- Removed patch ("limit parent .git directory search") as this version
replaces the use of git apply --recount with patch and recountdiff.
A side effect of this simplification was no longer needing this weird
hack. [Josh]
- Updated the patch that handles input patches that add files to also
support removing files, implement this by directly inspecting the
.patch +++ and --- header lines via two file lists [Josh]
- Implement two short-circuiting updates: validate patches for steps 1
and 2, and allow the user to omit patches for steps 3 and 4. This
combines the original 'fail-fast' patch and some related notes on the
v1 thread. [Josh]
- Since v2 replaces git apply with patch and recountdiff, there is no
need for a -z/--fuzz argument, it comes with GNU patch for free.
v1: https://lore.kernel.org/live-patching/CAPhsuW5qrueccM123YbTo2ZvP-Rf+0UT-goG6c5A8gXw7BsF3w@mail.gmail.com/T/#t
Joe Lawrence (13):
objtool/klp: honor SHF_MERGE entry alignment in elf_add_data()
objtool/klp: fix mkstemp() failure with long paths
livepatch/klp-build: support patches that add/remove files
livepatch/klp-build: switch to GNU patch and recountdiff
livepatch/klp-build: add grep-override function
livepatch/klp-build: add Makefile with check target
livepatch/klp-build: fix shellcheck complaints
livepatch/klp-build: improve short-circuit validation
livepatch/klp-build: fix version mismatch when short-circuiting
livepatch/klp-build: provide friendlier error messages
livepatch/klp-build: add terminal color output
livepatch/klp-build: report patch validation drift
livepatch/klp-build: don't look for changed objects in tools/
scripts/livepatch/Makefile | 20 +++++
scripts/livepatch/klp-build | 142 ++++++++++++++++++++++--------------
tools/objtool/elf.c | 25 +------
3 files changed, 110 insertions(+), 77 deletions(-)
create mode 100644 scripts/livepatch/Makefile
-- Joe
--
2.53.0
^ permalink raw reply
* [PATCH 8/8] livepatch: Add tests for klp-build toolchain
From: Song Liu @ 2026-02-12 19:22 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
Add selftests for the klp-build toolchain. This includes kernel side test
code and .patch files. The tests cover both livepatch to vmlinux and kernel
modules.
Check tools/testing/selftests/livepatch/test_patches/README for
instructions to run these tests.
Signed-off-by: Song Liu <song@kernel.org>
---
AI was used to wrote the test code and .patch files in this.
---
kernel/livepatch/Kconfig | 20 +++
kernel/livepatch/Makefile | 2 +
kernel/livepatch/tests/Makefile | 6 +
kernel/livepatch/tests/klp_test_module.c | 111 ++++++++++++++
kernel/livepatch/tests/klp_test_module.h | 8 +
kernel/livepatch/tests/klp_test_vmlinux.c | 138 ++++++++++++++++++
kernel/livepatch/tests/klp_test_vmlinux.h | 16 ++
kernel/livepatch/tests/klp_test_vmlinux_aux.c | 59 ++++++++
scripts/setlocalversion | 9 ++
.../selftests/livepatch/test_patches/README | 15 ++
.../test_patches/klp_test_hash_change.patch | 30 ++++
.../test_patches/klp_test_module.patch | 18 +++
.../klp_test_nonstatic_to_static.patch | 40 +++++
.../klp_test_static_to_nonstatic.patch | 39 +++++
.../test_patches/klp_test_vmlinux.patch | 18 +++
15 files changed, 529 insertions(+)
create mode 100644 kernel/livepatch/tests/Makefile
create mode 100644 kernel/livepatch/tests/klp_test_module.c
create mode 100644 kernel/livepatch/tests/klp_test_module.h
create mode 100644 kernel/livepatch/tests/klp_test_vmlinux.c
create mode 100644 kernel/livepatch/tests/klp_test_vmlinux.h
create mode 100644 kernel/livepatch/tests/klp_test_vmlinux_aux.c
create mode 100644 tools/testing/selftests/livepatch/test_patches/README
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_hash_change.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_module.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_nonstatic_to_static.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_static_to_nonstatic.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_vmlinux.patch
diff --git a/kernel/livepatch/Kconfig b/kernel/livepatch/Kconfig
index 4c0a9c18d0b2..852049601389 100644
--- a/kernel/livepatch/Kconfig
+++ b/kernel/livepatch/Kconfig
@@ -30,3 +30,23 @@ config KLP_BUILD
select OBJTOOL
help
Enable klp-build support
+
+config KLP_TEST
+ bool "Livepatch test code"
+ depends on LIVEPATCH
+ help
+ Dummy kernel code for testing the klp-build livepatch toolchain.
+ Provides built-in vmlinux functions with sysfs interfaces for
+ verifying livepatches.
+
+ If unsure, say N.
+
+config KLP_TEST_MODULE
+ tristate "Livepatch test module (klp_test_module)"
+ depends on KLP_TEST && m
+ help
+ Test module for livepatch testing. Dummy kernel module for
+ testing the klp-build toolchain. Provides sysfs interfaces for
+ verifying livepatches.
+
+ If unsure, say N.
diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile
index cf03d4bdfc66..751080a62cec 100644
--- a/kernel/livepatch/Makefile
+++ b/kernel/livepatch/Makefile
@@ -2,3 +2,5 @@
obj-$(CONFIG_LIVEPATCH) += livepatch.o
livepatch-objs := core.o patch.o shadow.o state.o transition.o
+
+obj-$(CONFIG_KLP_TEST) += tests/
diff --git a/kernel/livepatch/tests/Makefile b/kernel/livepatch/tests/Makefile
new file mode 100644
index 000000000000..82ae48f54abe
--- /dev/null
+++ b/kernel/livepatch/tests/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += klp_test_vmlinux_all.o
+obj-$(CONFIG_KLP_TEST_MODULE) += klp_test_module.o
+
+klp_test_vmlinux_all-y := klp_test_vmlinux.o \
+ klp_test_vmlinux_aux.o
diff --git a/kernel/livepatch/tests/klp_test_module.c b/kernel/livepatch/tests/klp_test_module.c
new file mode 100644
index 000000000000..25cefbe36a2b
--- /dev/null
+++ b/kernel/livepatch/tests/klp_test_module.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * klp_test_module.c - Single-file test module for livepatch/klp-build testing
+ *
+ * Copyright (C) 2026 Meta Platforms, Inc. and affiliates.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include "klp_test_module.h"
+#include "klp_test_vmlinux.h"
+
+static int klp_test_module_var1;
+static int klp_test_module_var2;
+
+static noinline ssize_t __klp_test_module_func1(char *buf, int len)
+{
+ ssize_t ret = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ klp_test_module_var1 += i;
+
+ if (klp_test_module_var1 > 1000)
+ klp_test_module_var1 = 0;
+
+ ret = sysfs_emit(buf, "klp_test_module_func1 unpatched %d\n",
+ klp_test_module_var1);
+ return ret;
+}
+
+ssize_t klp_test_module_func1(char *buf, int len)
+{
+ return __klp_test_module_func1(buf, len);
+}
+EXPORT_SYMBOL_GPL(klp_test_module_func1);
+
+static noinline ssize_t __klp_test_module_func2(char *buf, int len)
+{
+ ssize_t ret = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ klp_test_module_var2 += i * 2;
+
+ if (klp_test_module_var2 > 1000)
+ klp_test_module_var2 = 0;
+
+ ret = sysfs_emit(buf, "klp_test_module_func2 unpatched %d\n",
+ klp_test_module_var2);
+ return ret;
+}
+
+ssize_t klp_test_module_func2(char *buf, int len)
+{
+ return __klp_test_module_func2(buf, len);
+}
+EXPORT_SYMBOL_GPL(klp_test_module_func2);
+
+static ssize_t func1_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return klp_test_module_func1(buf, 5);
+}
+
+static ssize_t func2_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return klp_test_module_func2(buf, 5);
+}
+
+static struct kobj_attribute func1_attr = __ATTR_RO(func1);
+static struct kobj_attribute func2_attr = __ATTR_RO(func2);
+
+static struct attribute *klp_test_module_attrs[] = {
+ &func1_attr.attr,
+ &func2_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group klp_test_module_attr_group = {
+ .attrs = klp_test_module_attrs,
+};
+
+static struct kobject *klp_test_module_kobj;
+
+static int __init klp_test_module_init(void)
+{
+ klp_test_module_kobj = kobject_create_and_add("module",
+ klp_test_kobj);
+ if (!klp_test_module_kobj)
+ return -ENOMEM;
+
+ return sysfs_create_group(klp_test_module_kobj,
+ &klp_test_module_attr_group);
+}
+
+static void __exit klp_test_module_exit(void)
+{
+ sysfs_remove_group(klp_test_module_kobj, &klp_test_module_attr_group);
+ kobject_put(klp_test_module_kobj);
+}
+
+module_init(klp_test_module_init);
+module_exit(klp_test_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Livepatch single-file test module");
diff --git a/kernel/livepatch/tests/klp_test_module.h b/kernel/livepatch/tests/klp_test_module.h
new file mode 100644
index 000000000000..56a766f4744b
--- /dev/null
+++ b/kernel/livepatch/tests/klp_test_module.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _KLP_TEST_MODULE_H
+#define _KLP_TEST_MODULE_H
+
+ssize_t klp_test_module_func1(char *buf, int len);
+ssize_t klp_test_module_func2(char *buf, int len);
+
+#endif /* _KLP_TEST_MODULE_H */
diff --git a/kernel/livepatch/tests/klp_test_vmlinux.c b/kernel/livepatch/tests/klp_test_vmlinux.c
new file mode 100644
index 000000000000..bd4157ea97c0
--- /dev/null
+++ b/kernel/livepatch/tests/klp_test_vmlinux.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * klp_test_vmlinux.c - Dummy built-in code for livepatch/klp-build testing
+ *
+ * Copyright (C) 2026 Meta Platforms, Inc. and affiliates.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include "klp_test_vmlinux.h"
+
+static int klp_test_vmlinux_var1;
+static int klp_test_vmlinux_var2;
+
+static noinline int __helper(int x, int len)
+{
+ int i, sum = x;
+
+ for (i = 0; i < len; i++)
+ sum += i + 5;
+ if (sum > 1000)
+ sum = 0;
+ return sum;
+}
+
+static noinline ssize_t __klp_test_vmlinux_func1(char *buf, int len)
+{
+ ssize_t ret = 0;
+
+ klp_test_vmlinux_var1 = __helper(klp_test_vmlinux_var1, len);
+
+ ret = sysfs_emit(buf, "klp_test_vmlinux_func1 unpatched %d\n",
+ klp_test_vmlinux_var1);
+ return ret;
+}
+
+ssize_t klp_test_vmlinux_func1(char *buf, int len)
+{
+ return __klp_test_vmlinux_func1(buf, len);
+}
+EXPORT_SYMBOL_GPL(klp_test_vmlinux_func1);
+
+static noinline ssize_t __klp_test_vmlinux_func2(char *buf, int len)
+{
+ ssize_t ret = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ klp_test_vmlinux_var2 += i * 2;
+
+ if (klp_test_vmlinux_var2 > 1000)
+ klp_test_vmlinux_var2 = 0;
+
+ ret = sysfs_emit(buf, "klp_test_vmlinux_func2 unpatched %d\n",
+ klp_test_vmlinux_var2);
+ return ret;
+}
+
+ssize_t klp_test_vmlinux_func2(char *buf, int len)
+{
+ return __klp_test_vmlinux_func2(buf, len);
+}
+EXPORT_SYMBOL_GPL(klp_test_vmlinux_func2);
+
+static ssize_t vmlinux_func1_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return klp_test_vmlinux_func1(buf, 5);
+}
+
+static ssize_t vmlinux_func2_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return klp_test_vmlinux_func2(buf, 5);
+}
+
+static struct kobj_attribute vmlinux_func1_attr = __ATTR_RO(vmlinux_func1);
+static struct kobj_attribute vmlinux_func2_attr = __ATTR_RO(vmlinux_func2);
+
+static struct attribute *klp_test_attrs[] = {
+ &vmlinux_func1_attr.attr,
+ &vmlinux_func2_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group klp_test_attr_group = {
+ .attrs = klp_test_attrs,
+};
+
+static struct kobject *klp_test_vmlinux_kobj;
+struct kobject *klp_test_kobj;
+EXPORT_SYMBOL_GPL(klp_test_kobj);
+
+static int __init klp_test_vmlinux_init(void)
+{
+ int ret;
+
+ klp_test_kobj = kobject_create_and_add("klp_test", kernel_kobj);
+ if (!klp_test_kobj)
+ return -ENOMEM;
+
+ klp_test_vmlinux_kobj = kobject_create_and_add("vmlinux", klp_test_kobj);
+ if (!klp_test_vmlinux_kobj) {
+ kobject_put(klp_test_kobj);
+ return -ENOMEM;
+ }
+
+ ret = sysfs_create_group(klp_test_vmlinux_kobj, &klp_test_attr_group);
+ if (ret)
+ goto err_group;
+
+ ret = klp_test_vmlinux_aux_init(klp_test_vmlinux_kobj);
+ if (ret)
+ goto err_aux;
+
+ return 0;
+
+err_aux:
+ sysfs_remove_group(klp_test_vmlinux_kobj, &klp_test_attr_group);
+err_group:
+ kobject_put(klp_test_vmlinux_kobj);
+ kobject_put(klp_test_kobj);
+ return ret;
+}
+
+static void __exit klp_test_vmlinux_exit(void)
+{
+ klp_test_vmlinux_aux_exit(klp_test_vmlinux_kobj);
+ sysfs_remove_group(klp_test_vmlinux_kobj, &klp_test_attr_group);
+ kobject_put(klp_test_vmlinux_kobj);
+ kobject_put(klp_test_kobj);
+}
+
+late_initcall(klp_test_vmlinux_init);
diff --git a/kernel/livepatch/tests/klp_test_vmlinux.h b/kernel/livepatch/tests/klp_test_vmlinux.h
new file mode 100644
index 000000000000..56d9f7b6d350
--- /dev/null
+++ b/kernel/livepatch/tests/klp_test_vmlinux.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _KLP_TEST_VMLINUX_H
+#define _KLP_TEST_VMLINUX_H
+
+#include <linux/kobject.h>
+
+extern struct kobject *klp_test_kobj;
+
+ssize_t klp_test_vmlinux_func1(char *buf, int len);
+ssize_t klp_test_vmlinux_func2(char *buf, int len);
+ssize_t klp_test_vmlinux_func3(char *buf, int len);
+
+int klp_test_vmlinux_aux_init(struct kobject *parent);
+void klp_test_vmlinux_aux_exit(struct kobject *parent);
+
+#endif /* _KLP_TEST_VMLINUX_H */
diff --git a/kernel/livepatch/tests/klp_test_vmlinux_aux.c b/kernel/livepatch/tests/klp_test_vmlinux_aux.c
new file mode 100644
index 000000000000..1d76b0308a11
--- /dev/null
+++ b/kernel/livepatch/tests/klp_test_vmlinux_aux.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * klp_test_vmlinux_aux.c - Auxiliary built-in code for livepatch/klp-build
+ * testing. This file has its own static __helper()
+ * to test ThinLTO .llvm.<hash> suffix handling.
+ *
+ * Copyright (C) 2026 Meta Platforms, Inc. and affiliates.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include "klp_test_vmlinux.h"
+
+static int klp_test_vmlinux_var3;
+
+static noinline int __helper(int x, int len)
+{
+ int i, sum = x;
+
+ for (i = 0; i < len; i++)
+ sum += i + 10;
+ if (sum > 1000)
+ sum = 0;
+ return sum;
+}
+
+static noinline ssize_t __klp_test_vmlinux_func3(char *buf, int len)
+{
+ klp_test_vmlinux_var3 = __helper(klp_test_vmlinux_var3, len);
+
+ return sysfs_emit(buf, "klp_test_vmlinux_func3 unpatched %d\n",
+ klp_test_vmlinux_var3);
+}
+
+ssize_t klp_test_vmlinux_func3(char *buf, int len)
+{
+ return __klp_test_vmlinux_func3(buf, len);
+}
+EXPORT_SYMBOL_GPL(klp_test_vmlinux_func3);
+
+static ssize_t vmlinux_func3_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return klp_test_vmlinux_func3(buf, 5);
+}
+
+static struct kobj_attribute vmlinux_func3_attr = __ATTR_RO(vmlinux_func3);
+
+int klp_test_vmlinux_aux_init(struct kobject *parent)
+{
+ return sysfs_create_file(parent, &vmlinux_func3_attr.attr);
+}
+
+void klp_test_vmlinux_aux_exit(struct kobject *parent)
+{
+ sysfs_remove_file(parent, &vmlinux_func3_attr.attr);
+}
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index 28169d7e143b..6f28aedd253b 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -1,4 +1,13 @@
#!/bin/sh
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
+echo 6.19.0+; exit 0
# SPDX-License-Identifier: GPL-2.0
#
# This scripts adds local version information from the version
diff --git a/tools/testing/selftests/livepatch/test_patches/README b/tools/testing/selftests/livepatch/test_patches/README
new file mode 100644
index 000000000000..8266348aab57
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_patches/README
@@ -0,0 +1,15 @@
+This is folder contains patches to test the klp-build toolchain.
+
+To run the test:
+
+1. Enable CONFIG_KLP_TEST and CONFIG_KLP_TEST_MODULE, and build the kernel.
+
+2. Build these patches with:
+
+ ./scripts/livepatch/klp-build tools/testing/selftests/livepatch/test_patches/*.patch
+
+3. Verify the correctness with:
+
+ modprobe klp_test_module
+ kpatch load livepatch-patch.ko
+ grep -q unpatched /sys/kernel/klp_test/*/* && echo FAIL || echo PASS
diff --git a/tools/testing/selftests/livepatch/test_patches/klp_test_hash_change.patch b/tools/testing/selftests/livepatch/test_patches/klp_test_hash_change.patch
new file mode 100644
index 000000000000..609d54d6d6f6
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_patches/klp_test_hash_change.patch
@@ -0,0 +1,30 @@
+Test ThinLTO .llvm.<hash> suffix handling.
+
+Modify a static __helper() function whose body change causes its
+.llvm.<hash> suffix to change under ThinLTO. Both klp_test_vmlinux.c
+and klp_test_vmlinux_aux.c define static __helper() with different
+bodies, so ThinLTO promotes both to globals with different hashes.
+This patch changes the __helper() in the aux file, which changes its
+hash, and klp-build must correctly match the old and new symbols.
+
+diff --git i/kernel/livepatch/tests/klp_test_vmlinux_aux.c w/kernel/livepatch/tests/klp_test_vmlinux_aux.c
+--- i/kernel/livepatch/tests/klp_test_vmlinux_aux.c
++++ w/kernel/livepatch/tests/klp_test_vmlinux_aux.c
+@@ -20,7 +20,7 @@
+ int i, sum = x;
+
+ for (i = 0; i < len; i++)
+- sum += i + 10;
++ sum += i * 2 + 10;
+ if (sum > 1000)
+ sum = 0;
+ return sum;
+@@ -30,7 +30,7 @@
+ {
+ klp_test_vmlinux_var3 = __helper(klp_test_vmlinux_var3, len);
+
+- return sysfs_emit(buf, "klp_test_vmlinux_func3 unpatched %d\n",
++ return sysfs_emit(buf, "klp_test_vmlinux_func3 hash_patched %d\n",
+ klp_test_vmlinux_var3);
+ }
+
diff --git a/tools/testing/selftests/livepatch/test_patches/klp_test_module.patch b/tools/testing/selftests/livepatch/test_patches/klp_test_module.patch
new file mode 100644
index 000000000000..d86e75618136
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_patches/klp_test_module.patch
@@ -0,0 +1,18 @@
+Test basic module patching.
+
+Patch a loadable module function to verify that klp-build can generate
+a livepatch for module code. Changes __klp_test_module_func1() output
+from "unpatched" to "patched".
+
+diff --git i/kernel/livepatch/tests/klp_test_module.c w/kernel/livepatch/tests/klp_test_module.c
+--- i/kernel/livepatch/tests/klp_test_module.c
++++ w/kernel/livepatch/tests/klp_test_module.c
+@@ -27,7 +27,7 @@
+ if (klp_test_module_var1 > 1000)
+ klp_test_module_var1 = 0;
+
+- ret = sysfs_emit(buf, "klp_test_module_func1 unpatched %d\n",
++ ret = sysfs_emit(buf, "klp_test_module_func1 patched %d\n",
+ klp_test_module_var1);
+ return ret;
+ }
diff --git a/tools/testing/selftests/livepatch/test_patches/klp_test_nonstatic_to_static.patch b/tools/testing/selftests/livepatch/test_patches/klp_test_nonstatic_to_static.patch
new file mode 100644
index 000000000000..f26711c6bfac
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_patches/klp_test_nonstatic_to_static.patch
@@ -0,0 +1,40 @@
+Test nonstatic-to-static symbol change.
+
+Change klp_test_module_func2() from nonstatic (global) to static and
+remove its EXPORT_SYMBOL_GPL. Also remove its declaration from the
+header file. This tests klp-build's ability to handle symbol visibility
+changes where a function that was originally global becomes static in
+the patched kernel.
+
+diff --git i/kernel/livepatch/tests/klp_test_module.c w/kernel/livepatch/tests/klp_test_module.c
+--- i/kernel/livepatch/tests/klp_test_module.c
++++ w/kernel/livepatch/tests/klp_test_module.c
+@@ -49,16 +49,15 @@
+ if (klp_test_module_var2 > 1000)
+ klp_test_module_var2 = 0;
+
+- ret = sysfs_emit(buf, "klp_test_module_func2 unpatched %d\n",
++ ret = sysfs_emit(buf, "klp_test_module_func2 patched_nts %d\n",
+ klp_test_module_var2);
+ return ret;
+ }
+
+-ssize_t klp_test_module_func2(char *buf, int len)
++static noinline ssize_t klp_test_module_func2(char *buf, int len)
+ {
+ return __klp_test_module_func2(buf, len);
+ }
+-EXPORT_SYMBOL_GPL(klp_test_module_func2);
+
+ static ssize_t func1_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+diff --git i/kernel/livepatch/tests/klp_test_module.h w/kernel/livepatch/tests/klp_test_module.h
+--- i/kernel/livepatch/tests/klp_test_module.h
++++ w/kernel/livepatch/tests/klp_test_module.h
+@@ -3,6 +3,5 @@
+ #define _KLP_TEST_MODULE_H
+
+ ssize_t klp_test_module_func1(char *buf, int len);
+-ssize_t klp_test_module_func2(char *buf, int len);
+
+ #endif /* _KLP_TEST_MODULE_H */
diff --git a/tools/testing/selftests/livepatch/test_patches/klp_test_static_to_nonstatic.patch b/tools/testing/selftests/livepatch/test_patches/klp_test_static_to_nonstatic.patch
new file mode 100644
index 000000000000..673f6c42f698
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_patches/klp_test_static_to_nonstatic.patch
@@ -0,0 +1,39 @@
+Test static-to-nonstatic symbol change.
+
+Change __klp_test_vmlinux_func2() from static to nonstatic (global).
+This tests klp-build's ability to handle symbol visibility changes
+where a function that was originally static becomes globally visible
+in the patched kernel.
+
+diff --git i/kernel/livepatch/tests/klp_test_vmlinux.c w/kernel/livepatch/tests/klp_test_vmlinux.c
+--- i/kernel/livepatch/tests/klp_test_vmlinux.c
++++ w/kernel/livepatch/tests/klp_test_vmlinux.c
+@@ -44,7 +44,7 @@
+ }
+ EXPORT_SYMBOL_GPL(klp_test_vmlinux_func1);
+
+-static noinline ssize_t __klp_test_vmlinux_func2(char *buf, int len)
++noinline ssize_t __klp_test_vmlinux_func2(char *buf, int len)
+ {
+ ssize_t ret = 0;
+ int i;
+@@ -55,7 +55,7 @@
+ if (klp_test_vmlinux_var2 > 1000)
+ klp_test_vmlinux_var2 = 0;
+
+- ret = sysfs_emit(buf, "klp_test_vmlinux_func2 unpatched %d\n",
++ ret = sysfs_emit(buf, "klp_test_vmlinux_func2 patched_stn %d\n",
+ klp_test_vmlinux_var2);
+ return ret;
+ }
+diff --git i/kernel/livepatch/tests/klp_test_vmlinux.h w/kernel/livepatch/tests/klp_test_vmlinux.h
+--- i/kernel/livepatch/tests/klp_test_vmlinux.h
++++ w/kernel/livepatch/tests/klp_test_vmlinux.h
+@@ -9,6 +9,7 @@
+ ssize_t klp_test_vmlinux_func1(char *buf, int len);
+ ssize_t klp_test_vmlinux_func2(char *buf, int len);
+ ssize_t klp_test_vmlinux_func3(char *buf, int len);
++ssize_t __klp_test_vmlinux_func2(char *buf, int len);
+
+ int klp_test_vmlinux_aux_init(struct kobject *parent);
+ void klp_test_vmlinux_aux_exit(struct kobject *parent);
diff --git a/tools/testing/selftests/livepatch/test_patches/klp_test_vmlinux.patch b/tools/testing/selftests/livepatch/test_patches/klp_test_vmlinux.patch
new file mode 100644
index 000000000000..8b1d91381728
--- /dev/null
+++ b/tools/testing/selftests/livepatch/test_patches/klp_test_vmlinux.patch
@@ -0,0 +1,18 @@
+Test basic vmlinux patching.
+
+Patch a built-in vmlinux function to verify that klp-build can generate
+a livepatch for vmlinux code. Changes __klp_test_vmlinux_func1() output
+from "unpatched" to "patched".
+
+diff --git i/kernel/livepatch/tests/klp_test_vmlinux.c w/kernel/livepatch/tests/klp_test_vmlinux.c
+--- i/kernel/livepatch/tests/klp_test_vmlinux.c
++++ w/kernel/livepatch/tests/klp_test_vmlinux.c
+@@ -33,7 +33,7 @@
+
+ klp_test_vmlinux_var1 = __helper(klp_test_vmlinux_var1, len);
+
+- ret = sysfs_emit(buf, "klp_test_vmlinux_func1 unpatched %d\n",
++ ret = sysfs_emit(buf, "klp_test_vmlinux_func1 patched %d\n",
+ klp_test_vmlinux_var1);
+ return ret;
+ }
--
2.47.3
^ permalink raw reply related
* [PATCH 7/8] objtool/klp: Correlate locals to globals
From: Song Liu @ 2026-02-12 19:22 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
Allow correlating original locals to patched globals, and vice versa.
This is needed when:
1. User adds/removes "static" for a function.
2. CONFIG_LTO_CLANG_THIN promotes local functions and objects to global
and add .llvm.<hash> suffix.
Given this is a less common scenario, show warnings when this is needed.
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/klp-diff.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index cd82f674862a..f7a31ea2cbe7 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -483,6 +483,40 @@ static int correlate_symbols(struct elfs *e)
}
}
+ /* Correlate original locals with patched globals */
+ for_each_sym(e->orig, sym1) {
+ if (sym1->twin || dont_correlate(sym1) || !is_local_sym(sym1))
+ continue;
+ sym2 = find_global_symbol_by_name(e->patched, sym1->name);
+ if (!sym2) {
+ sym2 = find_global_symbol_by_demangled_name(e->patched,
+ sym1->demangled_name);
+ }
+ if (sym2 && !sym2->twin) {
+ sym1->twin = sym2;
+ sym2->twin = sym1;
+ WARN("correlate LOCAL %s (origial) to GLOBAL %s (patched)",
+ sym1->name, sym2->name);
+ }
+ }
+
+ /* Correlate original globals with patched locals */
+ for_each_sym(e->patched, sym2) {
+ if (sym2->twin || dont_correlate(sym2) || !is_local_sym(sym2))
+ continue;
+ sym1 = find_global_symbol_by_name(e->orig, sym2->name);
+ if (!sym1) {
+ sym1 = find_global_symbol_by_demangled_name(e->orig,
+ sym2->demangled_name);
+ }
+ if (sym1 && !sym1->twin) {
+ sym2->twin = sym1;
+ sym1->twin = sym2;
+ WARN("correlate GLOBAL %s (origial) to LOCAL %s (patched)",
+ sym1->name, sym2->name);
+ }
+ }
+
for_each_sym(e->orig, sym1) {
if (sym1->twin || dont_correlate(sym1))
continue;
--
2.47.3
^ permalink raw reply related
* [PATCH 6/8] objtool/klp: Match symbols based on demangled_name for global variables
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
correlate_symbols will always try to match full name first. If there is no
match, try match only demangled_name.
In very rare cases, it is possible to have multiple foo.llvm.<hash> in
the same kernel. So this match is not guaranteed to be correct. Show
a warning here so that the user can double check.
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/elf.c | 13 +++++++++++++
tools/objtool/include/objtool/elf.h | 2 ++
tools/objtool/klp-diff.c | 23 +++++++++++++++++++++++
3 files changed, 38 insertions(+)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index efb13ec0a89d..d26ee877e613 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -323,6 +323,19 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
return NULL;
}
+struct symbol *find_global_symbol_by_demangled_name(const struct elf *elf,
+ const char *demangled_name)
+{
+ struct symbol *sym;
+
+ elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(demangled_name)) {
+ if (!strcmp(sym->demangled_name, demangled_name) && !is_local_sym(sym))
+ return sym;
+ }
+
+ return NULL;
+}
+
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len)
{
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index e12c516bd320..f757850b8ff1 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -186,6 +186,8 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name);
+struct symbol *find_global_symbol_by_demangled_name(const struct elf *elf,
+ const char *demangled_name);
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 57606bc3390a..cd82f674862a 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -453,6 +453,29 @@ static int correlate_symbols(struct elfs *e)
continue;
sym2 = find_global_symbol_by_name(e->patched, sym1->name);
+ if (!sym2) {
+ /*
+ * No full name match, try match demangled_name.
+ * This would match foo.llvm.123 and foo.llvm.456.
+ *
+ * Note that, in very rare cases, it is possible
+ * to have multiple foo.llvm.<hash> in the same
+ * kernel, e.g., foo.llvm.123 in the original
+ * kernel, and both foo.llvm.456 and foo.llvm.789
+ * in the patched kernel. The correlation is not
+ * guaranteed to be correct in such cases.
+ *
+ * Show a warning to remind the user to double
+ * check the correlation.
+ */
+
+ sym2 = find_global_symbol_by_demangled_name(e->patched,
+ sym1->demangled_name);
+ if (sym2) {
+ WARN("correlate %s (origial) to %s (patched)",
+ sym1->name, sym2->name);
+ }
+ }
if (sym2 && !sym2->twin) {
sym1->twin = sym2;
--
2.47.3
^ permalink raw reply related
* [PATCH 5/8] objtool/klp: Remove .llvm suffix in demangle_name()
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
Remove .llvm suffix, so that we can correlate foo.llvm.<hash 1> and
foo.llvm.<hash 2>.
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/elf.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index d66452d66fb4..efb13ec0a89d 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -455,10 +455,15 @@ static int read_sections(struct elf *elf)
static ssize_t demangled_name_len(const char *name)
{
ssize_t len;
+ const char *p;
if (!strstarts(name, "__UNIQUE_ID_") && !strchr(name, '.'))
return strlen(name);
+ p = strstr(name, ".llvm.");
+ if (p)
+ return p - name;
+
for (len = strlen(name) - 1; len >= 0; len--) {
char c = name[len];
@@ -482,6 +487,9 @@ static ssize_t demangled_name_len(const char *name)
* __UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695
*
* to remove both trailing numbers, also remove trailing '_'.
+ *
+ * For symbols with llvm suffix, i.e., foo.llvm.<hash>, remove the
+ * .llvm.<hash> part.
*/
static const char *demangle_name(struct symbol *sym)
{
--
2.47.3
^ permalink raw reply related
* [PATCH 4/8] objtool/klp: Also demangle global objects
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
With CONFIG_LTO_CLANG_THIN, it is possible to have global __UNIQUE_ID,
such as:
FUNC GLOBAL HIDDEN 19745 __UNIQUE_ID_quirk_amd_nb_node_458
Also demangle global objects.
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/elf.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index c784a0484270..d66452d66fb4 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -488,9 +488,6 @@ static const char *demangle_name(struct symbol *sym)
char *str;
ssize_t len;
- if (!is_local_sym(sym))
- return sym->name;
-
if (!is_func_sym(sym) && !is_object_sym(sym))
return sym->name;
--
2.47.3
^ permalink raw reply related
* [PATCH 3/8] objtool/klp: Use sym->demangled_name for symbol_name hash
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
For klp-build with LTO, it is necessary to correlate demangled symbols,
e.g., correlate foo.llvm.<num 1> and foo.llvm.<num 2>. However, these two
symbols do not have the same str_hash(name). To be able to correlate the
two symbols, calculate hash based on demanged_name, so that these two
symbols have the same hash.
No functional changes intended.
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/elf.c | 58 +++++++++++++++++++++++++++++++--------------
1 file changed, 40 insertions(+), 18 deletions(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 0d93e8496e8d..c784a0484270 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -26,11 +26,18 @@
#include <objtool/elf.h>
#include <objtool/warn.h>
+static ssize_t demangled_name_len(const char *name);
+
static inline u32 str_hash(const char *str)
{
return jhash(str, strlen(str), 0);
}
+static inline u32 str_hash_demangled(const char *str)
+{
+ return jhash(str, demangled_name_len(str), 0);
+}
+
#define __elf_table(name) (elf->name##_hash)
#define __elf_bits(name) (elf->name##_bits)
@@ -294,7 +301,7 @@ static struct symbol *find_local_symbol_by_file_and_name(const struct elf *elf,
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
+ elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
if (sym->bind == STB_LOCAL && sym->file == file &&
!strcmp(sym->name, name)) {
return sym;
@@ -308,7 +315,7 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
+ elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
if (!strcmp(sym->name, name) && !is_local_sym(sym))
return sym;
}
@@ -441,6 +448,28 @@ static int read_sections(struct elf *elf)
return 0;
}
+/*
+ * Returns desired length of the demangled name.
+ * If name doesn't need demangling, return strlen(name).
+ */
+static ssize_t demangled_name_len(const char *name)
+{
+ ssize_t len;
+
+ if (!strstarts(name, "__UNIQUE_ID_") && !strchr(name, '.'))
+ return strlen(name);
+
+ for (len = strlen(name) - 1; len >= 0; len--) {
+ char c = name[len];
+
+ if (!isdigit(c) && c != '.' && c != '_')
+ break;
+ }
+ if (len <= 0)
+ return strlen(name);
+ return len;
+}
+
/*
* Remove number suffix of a symbol.
*
@@ -457,6 +486,7 @@ static int read_sections(struct elf *elf)
static const char *demangle_name(struct symbol *sym)
{
char *str;
+ ssize_t len;
if (!is_local_sym(sym))
return sym->name;
@@ -464,24 +494,16 @@ static const char *demangle_name(struct symbol *sym)
if (!is_func_sym(sym) && !is_object_sym(sym))
return sym->name;
- if (!strstarts(sym->name, "__UNIQUE_ID_") && !strchr(sym->name, '.'))
+ len = demangled_name_len(sym->name);
+ if (len == strlen(sym->name))
return sym->name;
- str = strdup(sym->name);
+ str = strndup(sym->name, len);
if (!str) {
ERROR_GLIBC("strdup");
return NULL;
}
- for (int i = strlen(str) - 1; i >= 0; i--) {
- char c = str[i];
-
- if (!isdigit(c) && c != '.' && c != '_') {
- str[i + 1] = '\0';
- break;
- }
- }
-
return str;
}
@@ -517,9 +539,13 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
entry = &sym->sec->symbol_list;
list_add(&sym->list, entry);
+ sym->demangled_name = demangle_name(sym);
+ if (!sym->demangled_name)
+ return -1;
+
list_add_tail(&sym->global_list, &elf->symbols);
elf_hash_add(symbol, &sym->hash, sym->idx);
- elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
+ elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->demangled_name));
if (is_func_sym(sym) &&
(strstarts(sym->name, "__pfx_") ||
@@ -543,10 +569,6 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)
sym->pfunc = sym->cfunc = sym;
- sym->demangled_name = demangle_name(sym);
- if (!sym->demangled_name)
- return -1;
-
return 0;
}
--
2.47.3
^ permalink raw reply related
* [PATCH 2/8] objtool/klp: Remove trailing '_' in demangle_name()
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
With CONFIG_LTO_CLANG_THIN, it is possible to have nested __UNIQUE_ID_,
such as:
__UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695
To remove both trailing numbers, also remove trailing '_'.
Also add comments to demangle_name().
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/elf.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 2c02c7b49265..0d93e8496e8d 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -441,6 +441,19 @@ static int read_sections(struct elf *elf)
return 0;
}
+/*
+ * Remove number suffix of a symbol.
+ *
+ * Specifically, remove trailing numbers for "__UNIQUE_ID_" symbols and
+ * symbols with '.'.
+ *
+ * With CONFIG_LTO_CLANG_THIN, it is possible to have nested __UNIQUE_ID_,
+ * such as
+ *
+ * __UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695
+ *
+ * to remove both trailing numbers, also remove trailing '_'.
+ */
static const char *demangle_name(struct symbol *sym)
{
char *str;
@@ -463,7 +476,7 @@ static const char *demangle_name(struct symbol *sym)
for (int i = strlen(str) - 1; i >= 0; i--) {
char c = str[i];
- if (!isdigit(c) && c != '.') {
+ if (!isdigit(c) && c != '.' && c != '_') {
str[i + 1] = '\0';
break;
}
--
2.47.3
^ permalink raw reply related
* [PATCH 1/8] objtool/klp: Remove redundent strcmp in correlate_symbols
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
In-Reply-To: <20260212192201.3593879-1-song@kernel.org>
find_global_symbol_by_name() already compares names of the two symbols,
so there is no need to compare them again.
Signed-off-by: Song Liu <song@kernel.org>
---
tools/objtool/klp-diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index a3198a63c2f0..57606bc3390a 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -454,7 +454,7 @@ static int correlate_symbols(struct elfs *e)
sym2 = find_global_symbol_by_name(e->patched, sym1->name);
- if (sym2 && !sym2->twin && !strcmp(sym1->name, sym2->name)) {
+ if (sym2 && !sym2->twin) {
sym1->twin = sym2;
sym2->twin = sym1;
}
--
2.47.3
^ permalink raw reply related
* [PATCH 0/8] objtool/klp: klp-build LTO support and tests
From: Song Liu @ 2026-02-12 19:21 UTC (permalink / raw)
To: live-patching
Cc: jpoimboe, jikos, mbenes, pmladek, joe.lawrence, kernel-team,
Song Liu
Add support for LTO in klp-build toolchain. The key changes are to the
symbol correlation logic.Basically, we want to:
1. Match symbols with differerent .llvm.<hash> suffixes, e.g., foo.llvm.123
to foo.llvm.456.
2. Match local symbols with promoted global symbols, e.g., local foo
with global foo.llvm.123.
1/8 and 2/8 are small cleanup/fix for existing code.
3/8 through 7/8 contains the core logic changes to correlate_symbols().
8/8 contains tests for klp-build toolchain.
Song Liu (8):
objtool/klp: Remove redundent strcmp in correlate_symbols
objtool/klp: Remove trailing '_' in demangle_name()
objtool/klp: Use sym->demangled_name for symbol_name hash
objtool/klp: Also demangle global objects
objtool/klp: Remove .llvm suffix in demangle_name()
objtool/klp: Match symbols based on demangled_name for global
variables
objtool/klp: Correlate locals to globals
livepatch: Add tests for klp-build toolchain
kernel/livepatch/Kconfig | 20 +++
kernel/livepatch/Makefile | 2 +
kernel/livepatch/tests/Makefile | 6 +
kernel/livepatch/tests/klp_test_module.c | 111 ++++++++++++++
kernel/livepatch/tests/klp_test_module.h | 8 +
kernel/livepatch/tests/klp_test_vmlinux.c | 138 ++++++++++++++++++
kernel/livepatch/tests/klp_test_vmlinux.h | 16 ++
kernel/livepatch/tests/klp_test_vmlinux_aux.c | 59 ++++++++
scripts/setlocalversion | 9 ++
tools/objtool/elf.c | 95 +++++++++---
tools/objtool/include/objtool/elf.h | 2 +
tools/objtool/klp-diff.c | 59 +++++++-
.../selftests/livepatch/test_patches/README | 15 ++
.../test_patches/klp_test_hash_change.patch | 30 ++++
.../test_patches/klp_test_module.patch | 18 +++
.../klp_test_nonstatic_to_static.patch | 40 +++++
.../klp_test_static_to_nonstatic.patch | 39 +++++
.../test_patches/klp_test_vmlinux.patch | 18 +++
18 files changed, 663 insertions(+), 22 deletions(-)
create mode 100644 kernel/livepatch/tests/Makefile
create mode 100644 kernel/livepatch/tests/klp_test_module.c
create mode 100644 kernel/livepatch/tests/klp_test_module.h
create mode 100644 kernel/livepatch/tests/klp_test_vmlinux.c
create mode 100644 kernel/livepatch/tests/klp_test_vmlinux.h
create mode 100644 kernel/livepatch/tests/klp_test_vmlinux_aux.c
create mode 100644 tools/testing/selftests/livepatch/test_patches/README
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_hash_change.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_module.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_nonstatic_to_static.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_static_to_nonstatic.patch
create mode 100644 tools/testing/selftests/livepatch/test_patches/klp_test_vmlinux.patch
--
2.47.3
^ permalink raw reply
* Re: [PATCH 0/3] objtool/klp: Special section validation fixes
From: Song Liu @ 2026-02-11 0:10 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: x86, linux-kernel, Peter Zijlstra, live-patching, Joe Lawrence
In-Reply-To: <cover.1770759954.git.jpoimboe@kernel.org>
On Tue, Feb 10, 2026 at 1:50 PM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
>
> Fix some issues in validate_special_section_klp_reloc().
>
> Josh Poimboeuf (3):
> objtool/klp: Fix detection of corrupt static branch/call entries
> objtool/klp: Disable unsupported pr_debug() usage
> objtool/klp: Avoid NULL pointer dereference when printing code symbol
> name
For the set
Reviewed-and-tested-by: Song Liu <song@kernel.org>
>
> tools/objtool/klp-diff.c | 39 ++++++++++++++++++++++++++-------------
> 1 file changed, 26 insertions(+), 13 deletions(-)
>
> --
> 2.53.0
>
^ permalink raw reply
* [PATCH 3/3] objtool/klp: Avoid NULL pointer dereference when printing code symbol name
From: Josh Poimboeuf @ 2026-02-10 21:50 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, Peter Zijlstra, live-patching, Song Liu,
Joe Lawrence
In-Reply-To: <cover.1770759954.git.jpoimboe@kernel.org>
Fix a hypothetical NULL pointer defereference of the 'code_sym'
variable. In theory this should never happen.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 9ff65b01882b..a3198a63c2f0 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1352,7 +1352,7 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
{
bool static_branch = !strcmp(sym->sec->name, "__jump_table");
bool static_call = !strcmp(sym->sec->name, ".static_call_sites");
- struct symbol *code_sym = NULL;
+ const char *code_sym = NULL;
unsigned long code_offset = 0;
struct reloc *reloc;
int ret = 0;
@@ -1372,7 +1372,7 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
/* Save code location which can be printed below */
if (reloc->sym->type == STT_FUNC && !code_sym) {
- code_sym = reloc->sym;
+ code_sym = reloc->sym->name;
code_offset = reloc_addend(reloc);
}
@@ -1395,23 +1395,26 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
if (!strcmp(sym_modname, "vmlinux"))
continue;
+ if (!code_sym)
+ code_sym = "<unknown>";
+
if (static_branch) {
if (strstarts(reloc->sym->name, "__tracepoint_")) {
WARN("%s: disabling unsupported tracepoint %s",
- code_sym->name, reloc->sym->name + 13);
+ code_sym, reloc->sym->name + 13);
ret = 1;
continue;
}
if (strstr(reloc->sym->name, "__UNIQUE_ID_ddebug_")) {
WARN("%s: disabling unsupported pr_debug()",
- code_sym->name);
+ code_sym);
ret = 1;
continue;
}
ERROR("%s+0x%lx: unsupported static branch key %s. Use static_key_enabled() instead",
- code_sym->name, code_offset, reloc->sym->name);
+ code_sym, code_offset, reloc->sym->name);
return -1;
}
@@ -1422,7 +1425,7 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
}
ERROR("%s()+0x%lx: unsupported static call key %s. Use KLP_STATIC_CALL() instead",
- code_sym->name, code_offset, reloc->sym->name);
+ code_sym, code_offset, reloc->sym->name);
return -1;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 2/3] objtool/klp: Disable unsupported pr_debug() usage
From: Josh Poimboeuf @ 2026-02-10 21:50 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, Peter Zijlstra, live-patching, Song Liu,
Joe Lawrence
In-Reply-To: <cover.1770759954.git.jpoimboe@kernel.org>
Instead of erroring out on unsupported pr_debug() (e.g., when patching a
module), issue a warning and make it inert, similar to how unsupported
tracepoints are currently handled.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index d94632e80955..9ff65b01882b 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1334,18 +1334,18 @@ static bool should_keep_special_sym(struct elf *elf, struct symbol *sym)
* be applied after static branch/call init, resulting in code corruption.
*
* Validate a special section entry to avoid that. Note that an inert
- * tracepoint is harmless enough, in that case just skip the entry and print a
- * warning. Otherwise, return an error.
+ * tracepoint or pr_debug() is harmless enough, in that case just skip the
+ * entry and print a warning. Otherwise, return an error.
*
- * This is only a temporary limitation which will be fixed when livepatch adds
- * support for submodules: fully self-contained modules which are embedded in
- * the top-level livepatch module's data and which can be loaded on demand when
- * their corresponding to-be-patched module gets loaded. Then klp relocs can
- * be retired.
+ * TODO: This is only a temporary limitation which will be fixed when livepatch
+ * adds support for submodules: fully self-contained modules which are embedded
+ * in the top-level livepatch module's data and which can be loaded on demand
+ * when their corresponding to-be-patched module gets loaded. Then klp relocs
+ * can be retired.
*
* Return:
* -1: error: validation failed
- * 1: warning: tracepoint skipped
+ * 1: warning: disabled tracepoint or pr_debug()
* 0: success
*/
static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym)
@@ -1403,6 +1403,13 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
continue;
}
+ if (strstr(reloc->sym->name, "__UNIQUE_ID_ddebug_")) {
+ WARN("%s: disabling unsupported pr_debug()",
+ code_sym->name);
+ ret = 1;
+ continue;
+ }
+
ERROR("%s+0x%lx: unsupported static branch key %s. Use static_key_enabled() instead",
code_sym->name, code_offset, reloc->sym->name);
return -1;
--
2.53.0
^ permalink raw reply related
* [PATCH 1/3] objtool/klp: Fix detection of corrupt static branch/call entries
From: Josh Poimboeuf @ 2026-02-10 21:50 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, Peter Zijlstra, live-patching, Song Liu,
Joe Lawrence
In-Reply-To: <cover.1770759954.git.jpoimboe@kernel.org>
Patching a function which references a static key living in a kernel
module is unsupported due to ordering issues inherent to late module
patching:
1) Load a livepatch module which has a __jump_table entry which needs
a klp reloc to reference static key K which lives in module M.
2) The __jump_table klp reloc does *not* get resolved because module M
is not yet loaded.
3) jump_label_add_module() corrupts memory (or causes a panic) when
dereferencing the uninitialized pointer to key K.
validate_special_section_klp_reloc() intends to prevent that from ever
happening by catching it at build time. However, it incorrectly assumes
the special section entry's reloc symbol references have already been
converted from section symbols to object symbols, causing the validation
to miss corruption in extracted static branch/call table entries.
Make sure the references have been properly converted before doing the
validation.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Reported-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 9f1f4011eb9c..d94632e80955 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1364,6 +1364,9 @@ static int validate_special_section_klp_reloc(struct elfs *e, struct symbol *sym
const char *sym_modname;
struct export *export;
+ if (convert_reloc_sym(e->patched, reloc))
+ continue;
+
/* Static branch/call keys are always STT_OBJECT */
if (reloc->sym->type != STT_OBJECT) {
--
2.53.0
^ permalink raw reply related
* [PATCH 0/3] objtool/klp: Special section validation fixes
From: Josh Poimboeuf @ 2026-02-10 21:50 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, Peter Zijlstra, live-patching, Song Liu,
Joe Lawrence
Fix some issues in validate_special_section_klp_reloc().
Josh Poimboeuf (3):
objtool/klp: Fix detection of corrupt static branch/call entries
objtool/klp: Disable unsupported pr_debug() usage
objtool/klp: Avoid NULL pointer dereference when printing code symbol
name
tools/objtool/klp-diff.c | 39 ++++++++++++++++++++++++++-------------
1 file changed, 26 insertions(+), 13 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH v2 2/5] livepatch/klp-build: handle patches that add/remove files
From: Josh Poimboeuf @ 2026-02-10 20:57 UTC (permalink / raw)
To: Joe Lawrence
Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <aYuNEwWoByiw0KDc@redhat.com>
On Tue, Feb 10, 2026 at 02:54:59PM -0500, Joe Lawrence wrote:
> On Thu, Feb 05, 2026 at 08:53:42AM -0800, Josh Poimboeuf wrote:
> > On Thu, Feb 05, 2026 at 11:35:00AM -0500, Joe Lawrence wrote:
> > > On Wed, Feb 04, 2026 at 10:02:38AM -0800, Josh Poimboeuf wrote:
> > > > On Tue, Feb 03, 2026 at 09:51:37PM -0500, Joe Lawrence wrote:
> > > > > The klp-build script prepares a clean patch by populating two temporary
> > > > > directories ('a' and 'b') with source files and diffing the result.
> > > > > However, this process currently fails when a patch introduces a new
> > > > > source file as the script attempts to copy files that do not yet exist
> > > > > in the original source tree. Likewise, there is a similar limitation
> > > > > when a patch removes a source file and the script tries to copy files
> > > > > that no longer exist.
> > > > >
> > > > > Refactor the file-gathering logic to distinguish between original input
> > > > > files and patched output files:
> > > > >
> > > > > - Split get_patch_files() into get_patch_input_files() and
> > > > > get_patch_output_files() to identify which files exist before and
> > > > > after patch application.
> > > > > - Filter out "/dev/null" from both to handle file creation/deletion
> > > > > - Update refresh_patch() to only copy existing input files to the 'a'
> > > > > directory and the resulting output files to the 'b' directory.
> > > > >
> > > > > This allows klp-build to successfully process patches that add or remove
> > > > > source files.
> > > > >
> > > > > Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
> > > > > ---
> > > > > scripts/livepatch/klp-build | 34 +++++++++++++++++++++++++++-------
> > > > > 1 file changed, 27 insertions(+), 7 deletions(-)
> > > > >
> > > > > Lightly tested with patches that added or removed a source file, as
> > > > > generated by `git diff`, `git format-patch`, and `diff -Nupr`.
> > > > >
> > > > > diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
> > > > > index 9f1b77c2b2b7..5a99ff4c4729 100755
> > > > > --- a/scripts/livepatch/klp-build
> > > > > +++ b/scripts/livepatch/klp-build
> > > > > @@ -299,15 +299,33 @@ set_kernelversion() {
> > > > > sed -i "2i echo $localversion; exit 0" scripts/setlocalversion
> > > > > }
> > > > >
> > > > > -get_patch_files() {
> > > > > +get_patch_input_files() {
> > > > > + local patch="$1"
> > > > > +
> > > > > + grep0 -E '^--- ' "$patch" \
> > > > > + | gawk '{print $2}' \
> > > > > + | grep -v '^/dev/null$' \
> > > >
> > > > Because pipefail is enabled, the grep0 helper should be used instead of
> > > > grep, otherwise a failed match can propagate to an error. Maybe we need
> > > > a "make check" or something which enforces that and runs shellcheck.
> > > >
>
> How about defining our own grep in the script that intercepts the call
> and throws an error:
>
> grep() {
> echo "error: $SCRIPT: use grep0 or 'command grep' instead of bare grep" >&2
> exit 1
> }
>
> That seems easier than trying to externally parse the script to figure
> out what's a command, comment, word-match, legit grep, etc.
Ack, sounds good.
--
Josh
^ permalink raw reply
* Re: [PATCH v2 2/5] livepatch/klp-build: handle patches that add/remove files
From: Joe Lawrence @ 2026-02-10 19:54 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <uy6a5xdp7e6cp6xj2r5zavb2ujtkapsjp2hixtgga4vwywkslv@6uhrgvb2hvo3>
On Thu, Feb 05, 2026 at 08:53:42AM -0800, Josh Poimboeuf wrote:
> On Thu, Feb 05, 2026 at 11:35:00AM -0500, Joe Lawrence wrote:
> > On Wed, Feb 04, 2026 at 10:02:38AM -0800, Josh Poimboeuf wrote:
> > > On Tue, Feb 03, 2026 at 09:51:37PM -0500, Joe Lawrence wrote:
> > > > The klp-build script prepares a clean patch by populating two temporary
> > > > directories ('a' and 'b') with source files and diffing the result.
> > > > However, this process currently fails when a patch introduces a new
> > > > source file as the script attempts to copy files that do not yet exist
> > > > in the original source tree. Likewise, there is a similar limitation
> > > > when a patch removes a source file and the script tries to copy files
> > > > that no longer exist.
> > > >
> > > > Refactor the file-gathering logic to distinguish between original input
> > > > files and patched output files:
> > > >
> > > > - Split get_patch_files() into get_patch_input_files() and
> > > > get_patch_output_files() to identify which files exist before and
> > > > after patch application.
> > > > - Filter out "/dev/null" from both to handle file creation/deletion
> > > > - Update refresh_patch() to only copy existing input files to the 'a'
> > > > directory and the resulting output files to the 'b' directory.
> > > >
> > > > This allows klp-build to successfully process patches that add or remove
> > > > source files.
> > > >
> > > > Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
> > > > ---
> > > > scripts/livepatch/klp-build | 34 +++++++++++++++++++++++++++-------
> > > > 1 file changed, 27 insertions(+), 7 deletions(-)
> > > >
> > > > Lightly tested with patches that added or removed a source file, as
> > > > generated by `git diff`, `git format-patch`, and `diff -Nupr`.
> > > >
> > > > diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
> > > > index 9f1b77c2b2b7..5a99ff4c4729 100755
> > > > --- a/scripts/livepatch/klp-build
> > > > +++ b/scripts/livepatch/klp-build
> > > > @@ -299,15 +299,33 @@ set_kernelversion() {
> > > > sed -i "2i echo $localversion; exit 0" scripts/setlocalversion
> > > > }
> > > >
> > > > -get_patch_files() {
> > > > +get_patch_input_files() {
> > > > + local patch="$1"
> > > > +
> > > > + grep0 -E '^--- ' "$patch" \
> > > > + | gawk '{print $2}' \
> > > > + | grep -v '^/dev/null$' \
> > >
> > > Because pipefail is enabled, the grep0 helper should be used instead of
> > > grep, otherwise a failed match can propagate to an error. Maybe we need
> > > a "make check" or something which enforces that and runs shellcheck.
> > >
How about defining our own grep in the script that intercepts the call
and throws an error:
grep() {
echo "error: $SCRIPT: use grep0 or 'command grep' instead of bare grep" >&2
exit 1
}
That seems easier than trying to externally parse the script to figure
out what's a command, comment, word-match, legit grep, etc.
--
Joe
^ permalink raw reply
* Re: [PATCH] klp: use stop machine to check and expedite transition for running tasks
From: Peter Zijlstra @ 2026-02-09 19:12 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: Li Zhe, jikos, mbenes, pmladek, joe.lawrence, live-patching,
linux-kernel, qirui.001, vschneid, dave.hansen
In-Reply-To: <5vctv762jvnxiselc3vwattfsgegw6uv7kltsp27qtoajel2rl@kjrg4ko74gcn>
On Tue, Feb 03, 2026 at 06:20:22PM -0800, Josh Poimboeuf wrote:
> On Mon, Feb 02, 2026 at 05:13:34PM +0800, Li Zhe wrote:
> > In the current KLP transition implementation, the strategy for running
> > tasks relies on waiting for a context switch to attempt to clear the
> > TIF_PATCH_PENDING flag. Alternatively, determine whether the
> > TIF_PATCH_PENDING flag can be cleared by inspecting the stack once the
> > process has yielded the CPU. However, this approach proves problematic
> > in certain environments.
> >
> > Consider a scenario where the majority of system CPUs are configured
> > with nohzfull and isolcpus, each dedicated to a VM with a vCPU pinned
> > to that physical core and configured with idle=poll within the guest.
> > Under such conditions, these vCPUs rarely leave the CPU. Combined with
> > the high core counts typical of modern server platforms, this results
> > in transition completion times that are not only excessively prolonged
> > but also highly unpredictable.
> >
> > This patch resolves this issue by registering a callback with
> > stop_machine. The callback attempts to transition the associated running
> > task. In a VM environment configured with 32 CPUs, the live patching
> > operation completes promptly after the SIGNALS_TIMEOUT period with this
> > patch applied; without it, the process nearly fails to complete under
> > the same scenario.
> >
> > Co-developed-by: Rui Qi <qirui.001@bytedance.com>
> > Signed-off-by: Rui Qi <qirui.001@bytedance.com>
> > Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
>
> PeterZ, what's your take on this?
>
> I wonder if we could instead do resched_cpu() or something similar to
> trigger the call to klp_sched_try_switch() in __schedule()?
Yeah, this is broken. So the whole point of NOHZ_FULL is to not have the
CPU disturbed, *ever*.
People are working really hard to remove any and all disturbance from
these CPUs with the eventual goal of making any disturbance a fatal
condition (userspace will get a fatal signal if disturbed or so).
Explicitly adding disturbance to NOHZ_FULL is an absolute no-no.
NAK
There are two ways this can be solved:
1) make it a user problem -- userspace wants to load kernel patch,
userspace can force their QEMU or whatnot through a system call to make
progress
2) fix it properly and do it like the deferred IPI stuff; recognise
that as long as the task is in userspace, it doesn't care about kernel
text changes.
https://lkml.kernel.org/r/20251114150133.1056710-1-vschneid@redhat.com
While 2 sounds easy, the tricky comes from the fact that you have to
deal with the task coming back to kernel space eventually, possibly in
the middle of your KLP patching. So you've got to do thing like that
patch series above, and make sure the whole of KLP happens while the
other CPU is in USER/GUEST context or waits for things when it tries to
leave while things are in progress.
^ permalink raw reply
* Re: [PATCH] klp: use stop machine to check and expedite transition for running tasks
From: Li Zhe @ 2026-02-09 2:54 UTC (permalink / raw)
To: lizhe.67
Cc: jikos, joe.lawrence, jpoimboe, linux-kernel, live-patching,
mbenes, peterz, pmladek, qirui.001
In-Reply-To: <20260204024756.6776-1-lizhe.67@bytedance.com>
On Mon, 2 Feb 2026 17:13:34 +0800, lizhe.67@bytedance.com wrote:
> In the current KLP transition implementation, the strategy for running
> tasks relies on waiting for a context switch to attempt to clear the
> TIF_PATCH_PENDING flag. Alternatively, determine whether the
> TIF_PATCH_PENDING flag can be cleared by inspecting the stack once the
> process has yielded the CPU. However, this approach proves problematic
> in certain environments.
>
> Consider a scenario where the majority of system CPUs are configured
> with nohzfull and isolcpus, each dedicated to a VM with a vCPU pinned
> to that physical core and configured with idle=poll within the guest.
> Under such conditions, these vCPUs rarely leave the CPU. Combined with
> the high core counts typical of modern server platforms, this results
> in transition completion times that are not only excessively prolonged
> but also highly unpredictable.
>
> This patch resolves this issue by registering a callback with
> stop_machine. The callback attempts to transition the associated running
> task. In a VM environment configured with 32 CPUs, the live patching
> operation completes promptly after the SIGNALS_TIMEOUT period with this
> patch applied; without it, the process nearly fails to complete under
> the same scenario.
>
> Co-developed-by: Rui Qi <qirui.001@bytedance.com>
> Signed-off-by: Rui Qi <qirui.001@bytedance.com>
> Signed-off-by: Li Zhe <lizhe.67@bytedance.com>
> ---
> kernel/livepatch/transition.c | 62 ++++++++++++++++++++++++++++++++---
> 1 file changed, 58 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
> index 2351a19ac2a9..9c078b9bd755 100644
> --- a/kernel/livepatch/transition.c
> +++ b/kernel/livepatch/transition.c
> @@ -10,6 +10,7 @@
> #include <linux/cpu.h>
> #include <linux/stacktrace.h>
> #include <linux/static_call.h>
> +#include <linux/stop_machine.h>
> #include "core.h"
> #include "patch.h"
> #include "transition.h"
> @@ -297,6 +298,61 @@ static int klp_check_and_switch_task(struct task_struct *task, void *arg)
> return 0;
> }
>
> +enum klp_stop_work_bit {
> + KLP_STOP_WORK_PENDING_BIT,
> +};
> +
> +struct klp_stop_work_info {
> + struct task_struct *task;
> + unsigned long flag;
> +};
> +
> +static DEFINE_PER_CPU(struct cpu_stop_work, klp_transition_stop_work);
> +static DEFINE_PER_CPU(struct klp_stop_work_info, klp_stop_work_info);
> +
> +static int klp_check_task(struct task_struct *task, void *old_name)
> +{
> + if (task == current)
> + return klp_check_and_switch_task(current, old_name);
> + else
> + return task_call_func(task, klp_check_and_switch_task, old_name);
> +}
> +
> +static int klp_transition_stop_work_fn(void *arg)
> +{
> + struct klp_stop_work_info *info = (struct klp_stop_work_info *)arg;
> + struct task_struct *task = info->task;
> + const char *old_name;
> +
> + clear_bit(KLP_STOP_WORK_PENDING_BIT, &info->flag);
> +
> + if (likely(klp_patch_pending(task)))
> + klp_check_task(task, &old_name);
> +
> + put_task_struct(task);
> +
> + return 0;
> +}
> +
> +static void klp_try_transition_running_task(struct task_struct *task)
> +{
> + int cpu = task_cpu(task);
> +
> + if (klp_signals_cnt && !(klp_signals_cnt % SIGNALS_TIMEOUT)) {
> + struct klp_stop_work_info *info =
> + per_cpu_ptr(&klp_stop_work_info, cpu);
> +
> + if (test_and_set_bit(KLP_STOP_WORK_PENDING_BIT, &info->flag))
> + return;
> +
> + info->task = get_task_struct(task);
> + if (!stop_one_cpu_nowait(cpu, klp_transition_stop_work_fn, info,
> + per_cpu_ptr(&klp_transition_stop_work,
> + cpu)))
> + put_task_struct(task);
> + }
> +}
> +
> /*
> * Try to safely switch a task to the target patch state. If it's currently
> * running, or it's sleeping on a to-be-patched or to-be-unpatched function, or
> @@ -323,10 +379,7 @@ static bool klp_try_switch_task(struct task_struct *task)
> * functions. If all goes well, switch the task to the target patch
> * state.
> */
> - if (task == current)
> - ret = klp_check_and_switch_task(current, &old_name);
> - else
> - ret = task_call_func(task, klp_check_and_switch_task, &old_name);
> + ret = klp_check_task(task, &old_name);
>
> switch (ret) {
> case 0: /* success */
> @@ -335,6 +388,7 @@ static bool klp_try_switch_task(struct task_struct *task)
> case -EBUSY: /* klp_check_and_switch_task() */
> pr_debug("%s: %s:%d is running\n",
> __func__, task->comm, task->pid);
> + klp_try_transition_running_task(task);
> break;
> case -EINVAL: /* klp_check_and_switch_task() */
> pr_debug("%s: %s:%d has an unreliable stack\n",
> --
> 2.20.1
Hi all,
Just a gentle ping on this patch.
Please let me know if there's anything I can improve or if you need
more information.
Thanks,
Zhe
^ permalink raw reply
* Re: [PATCH v2 3/5] livepatch/klp-build: switch to GNU patch and recountdiff
From: Josh Poimboeuf @ 2026-02-05 17:49 UTC (permalink / raw)
To: Joe Lawrence
Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <aYTS_ZtgcnZP3UCm@redhat.com>
On Thu, Feb 05, 2026 at 12:27:25PM -0500, Joe Lawrence wrote:
> On Wed, Feb 04, 2026 at 10:35:07AM -0800, Josh Poimboeuf wrote:
> > On Tue, Feb 03, 2026 at 09:51:38PM -0500, Joe Lawrence wrote:
> > > My initial thought was that I'd only be interested in knowing about
> > > patch offset/fuzz during the validation phase. And in the interest of
> > > clarifying some of the output messages, it would be nice to know the
> > > patch it was referring to, so how about a follow up patch
> > > pretty-formatting with some indentation like:
> > >
> > > Validating patch(es)
> > > cmdline-string.patch
> > > patching file fs/proc/cmdline.c
> > > Hunk #1 succeeded at 7 (offset 1 line).
> > > Fixing patch(es)
> > > Building patched kernel
> > > Copying patched object files
> > > Diffing objects
> > > vmlinux.o: changed function: override_release
> > > vmlinux.o: changed function: cmdline_proc_show
> > > Building patch module: livepatch-cmdline-string.ko
> > > SUCCESS
> > >
> > > That said, Song suggested using --silent across the board, so maybe
> > > tie that into the existing --verbose option?
> >
> > Hm. Currently we go to considerable effort to make klp-build's output
> > as concise as possible, which is good. On the other hand, it might be
> > important to know the patch has fuzz.
> >
>
> To keep it succinct, the script could check for offset/fuzz and only
> report it, including the "patching file ..." part, if there is any.
Maybe? Only if it's not too complicated.
> > I'm thinking I would agree that maybe it should be verbose when
> > validating patches and silent elsewhere. And the pretty formatting is a
> > nice upgrade to that.
> >
>
> In the past I've used a little function like:
>
> indent() {
> local num="${1:-0}"
> sed "s/^/$(printf '%*s' "$num" '')/"
> }
>
> so I could just pipe in echo or command output like: `./cmd | indent 2`.
> Good enough or maybe you have one?
Sounds good, it probably needs a "return true" at the end of the function.
> > We might also consider indenting the outputs of the other steps. For
> > example:
> >
> > Building patched kernel
> > vmlinux.o: some warning
> > Copying patched object files
> > Diffing objects
> > vmlinux.o: changed function: override_release
> > vmlinux.o: changed function: cmdline_proc_show
> >
> > Or alternatively, print the step names in ASCII bold or something.
> >
>
> While I do kinda like the recent color coded output from the compilers,
> I don't know if I'm ready for a full-color livepatch build experience :D
>
> I wouldn't be against it, but my vote leans towards the indentation
> since it leaves prettier log files, even if the color codes are filtered
> out. Then again, the color scheme bikeshedding we could look forward
> to!
:-)
--
Josh
^ permalink raw reply
* Re: [PATCH v2 4/5] livepatch/klp-build: minor short-circuiting tweaks
From: Joe Lawrence @ 2026-02-05 17:47 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <njg3ylqbsk3dc6smj6vnrk2bb7ttjrfsulfzocmh4fsdq527fj@xgoaep6sbqws>
On Wed, Feb 04, 2026 at 10:40:14AM -0800, Josh Poimboeuf wrote:
> On Tue, Feb 03, 2026 at 09:51:39PM -0500, Joe Lawrence wrote:
> > Update SHORT_CIRCUIT behavior to better handle patch validation and
> > argument processing in later klp-build steps.
> >
> > Perform patch validation for both step 1 (building original kernel)
> > and step 2 (building patched kernel) to ensure patches are verified
> > before any compilation occurs.
> >
> > Additionally, allow the user to omit input patches when skipping past
> > step 2, while noting that any specified patches will be ignored in that
> > case if they were provided.
> >
> > Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
> > ---
> > scripts/livepatch/klp-build | 17 +++++++++++++----
> > 1 file changed, 13 insertions(+), 4 deletions(-)
> >
> > diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
> > index ee43a9caa107..df3a0fa031a6 100755
> > --- a/scripts/livepatch/klp-build
> > +++ b/scripts/livepatch/klp-build
> > @@ -214,12 +214,18 @@ process_args() {
> > done
> >
> > if [[ $# -eq 0 ]]; then
> > - usage
> > - exit 1
> > + if (( SHORT_CIRCUIT <= 2 )); then
> > + usage
> > + exit 1
> > + fi
>
> Ack
>
> > + else
> > + if (( SHORT_CIRCUIT >= 3 )); then
> > + status "note: patch arguments ignored at step $SHORT_CIRCUIT"
> > + fi
>
> Personally I don't care to see this status, but maybe I'm biased from
> writing the --short-circuit feature and not being confused by this :-)
>
Alrighty, I'll drop this part unless somebody asks for it.
--
Joe
^ permalink raw reply
* Re: [PATCH v2 3/5] livepatch/klp-build: switch to GNU patch and recountdiff
From: Joe Lawrence @ 2026-02-05 17:27 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <2j5d3dwa6jymmnte4gcykbm5pfzc36x7onn2ojgjliwkxnlcik@34hti52xld5m>
On Wed, Feb 04, 2026 at 10:35:07AM -0800, Josh Poimboeuf wrote:
> On Tue, Feb 03, 2026 at 09:51:38PM -0500, Joe Lawrence wrote:
> > I think this does simplify things, but:
> >
> > - introduces a dependency on patchutil's recountdiff
>
> I was wondering if we could instead just update fix-patch-lines to fix
> the line numbers/counts, but I get the feeling that would require
> rewriting the whole script and may not be worth the complexity. That
> script is nice and simple and robust at the moment.
>
Ok, I'll take a look at fix-patch-lines and see how complicated it might
turn out to incorporate a recount.
> > - requires goofy epoch timestamp filtering as `diff -N` doesn't use
> > the `git diff` /dev/null, but a localized beginining of time epoch
> > that may be 1969 or 1970 depending on the local timezone
> > - can be *really* chatty, for example:
> >
> > Validating patch(es)
> > patching file fs/proc/cmdline.c
> > Hunk #1 succeeded at 7 (offset 1 line).
> > Fixing patch(es)
> > patching file fs/proc/cmdline.c
> > Hunk #1 succeeded at 7 (offset 1 line).
> > patching file fs/proc/cmdline.c
> > patching file fs/proc/cmdline.c
> > Building patched kernel
> > Copying patched object files
> > Diffing objects
> > vmlinux.o: changed function: override_release
> > vmlinux.o: changed function: cmdline_proc_show
> > Building patch module: livepatch-cmdline-string.ko
> > SUCCESS
> >
> > My initial thought was that I'd only be interested in knowing about
> > patch offset/fuzz during the validation phase. And in the interest of
> > clarifying some of the output messages, it would be nice to know the
> > patch it was referring to, so how about a follow up patch
> > pretty-formatting with some indentation like:
> >
> > Validating patch(es)
> > cmdline-string.patch
> > patching file fs/proc/cmdline.c
> > Hunk #1 succeeded at 7 (offset 1 line).
> > Fixing patch(es)
> > Building patched kernel
> > Copying patched object files
> > Diffing objects
> > vmlinux.o: changed function: override_release
> > vmlinux.o: changed function: cmdline_proc_show
> > Building patch module: livepatch-cmdline-string.ko
> > SUCCESS
> >
> > That said, Song suggested using --silent across the board, so maybe
> > tie that into the existing --verbose option?
>
> Hm. Currently we go to considerable effort to make klp-build's output
> as concise as possible, which is good. On the other hand, it might be
> important to know the patch has fuzz.
>
To keep it succinct, the script could check for offset/fuzz and only
report it, including the "patching file ..." part, if there is any.
> I'm thinking I would agree that maybe it should be verbose when
> validating patches and silent elsewhere. And the pretty formatting is a
> nice upgrade to that.
>
In the past I've used a little function like:
indent() {
local num="${1:-0}"
sed "s/^/$(printf '%*s' "$num" '')/"
}
so I could just pipe in echo or command output like: `./cmd | indent 2`.
Good enough or maybe you have one?
> We might also consider indenting the outputs of the other steps. For
> example:
>
> Building patched kernel
> vmlinux.o: some warning
> Copying patched object files
> Diffing objects
> vmlinux.o: changed function: override_release
> vmlinux.o: changed function: cmdline_proc_show
>
> Or alternatively, print the step names in ASCII bold or something.
>
While I do kinda like the recent color coded output from the compilers,
I don't know if I'm ready for a full-color livepatch build experience :D
I wouldn't be against it, but my vote leans towards the indentation
since it leaves prettier log files, even if the color codes are filtered
out. Then again, the color scheme bikeshedding we could look forward
to!
> > apply_patch() {
> > local patch="$1"
> > - shift
> > - local extra_args=("$@")
> >
> > [[ ! -f "$patch" ]] && die "$patch doesn't exist"
> >
> > (
> > cd "$SRC"
> > -
> > - # The sed strips the version signature from 'git format-patch',
> > - # otherwise 'git apply --recount' warns.
> > - sed -n '/^-- /q;p' "$patch" |
> > - git apply "${extra_args[@]}"
> > + # The sed strips the version signature from 'git format-patch'.
> > + sed -n '/^-- /q;p' "$patch" | \
> > + patch -p1 --no-backup-if-mismatch -r /dev/null
>
> Is this still needed now that we don't use git apply --recount?
>
I'll double check. I recall having difficulties with recountdiff when
taking these out, but can't reproduce that by hand at the moment.
> > @@ -490,7 +468,7 @@ fix_patches() {
> >
> > cp -f "$old_patch" "$tmp_patch"
> > refresh_patch "$tmp_patch"
> > - "$FIX_PATCH_LINES" "$tmp_patch" > "$new_patch"
> > + "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$new_patch"
> > refresh_patch "$new_patch"
>
> Do we still need to refresh after the recountdiff?
>
Yeah I think it's redundant as long as recountdiff doesn't emit
something weird, but if it did, it's probably already screwed up the
patch.
--
Joe
^ permalink raw reply
* Re: [PATCH v2 2/5] livepatch/klp-build: handle patches that add/remove files
From: Josh Poimboeuf @ 2026-02-05 16:53 UTC (permalink / raw)
To: Joe Lawrence
Cc: live-patching, Song Liu, Jiri Kosina, Miroslav Benes, Petr Mladek
In-Reply-To: <aYTGtI41jhDSm5gf@redhat.com>
On Thu, Feb 05, 2026 at 11:35:00AM -0500, Joe Lawrence wrote:
> On Wed, Feb 04, 2026 at 10:02:38AM -0800, Josh Poimboeuf wrote:
> > On Tue, Feb 03, 2026 at 09:51:37PM -0500, Joe Lawrence wrote:
> > > The klp-build script prepares a clean patch by populating two temporary
> > > directories ('a' and 'b') with source files and diffing the result.
> > > However, this process currently fails when a patch introduces a new
> > > source file as the script attempts to copy files that do not yet exist
> > > in the original source tree. Likewise, there is a similar limitation
> > > when a patch removes a source file and the script tries to copy files
> > > that no longer exist.
> > >
> > > Refactor the file-gathering logic to distinguish between original input
> > > files and patched output files:
> > >
> > > - Split get_patch_files() into get_patch_input_files() and
> > > get_patch_output_files() to identify which files exist before and
> > > after patch application.
> > > - Filter out "/dev/null" from both to handle file creation/deletion
> > > - Update refresh_patch() to only copy existing input files to the 'a'
> > > directory and the resulting output files to the 'b' directory.
> > >
> > > This allows klp-build to successfully process patches that add or remove
> > > source files.
> > >
> > > Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
> > > ---
> > > scripts/livepatch/klp-build | 34 +++++++++++++++++++++++++++-------
> > > 1 file changed, 27 insertions(+), 7 deletions(-)
> > >
> > > Lightly tested with patches that added or removed a source file, as
> > > generated by `git diff`, `git format-patch`, and `diff -Nupr`.
> > >
> > > diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
> > > index 9f1b77c2b2b7..5a99ff4c4729 100755
> > > --- a/scripts/livepatch/klp-build
> > > +++ b/scripts/livepatch/klp-build
> > > @@ -299,15 +299,33 @@ set_kernelversion() {
> > > sed -i "2i echo $localversion; exit 0" scripts/setlocalversion
> > > }
> > >
> > > -get_patch_files() {
> > > +get_patch_input_files() {
> > > + local patch="$1"
> > > +
> > > + grep0 -E '^--- ' "$patch" \
> > > + | gawk '{print $2}' \
> > > + | grep -v '^/dev/null$' \
> >
> > Because pipefail is enabled, the grep0 helper should be used instead of
> > grep, otherwise a failed match can propagate to an error. Maybe we need
> > a "make check" or something which enforces that and runs shellcheck.
> >
>
> Good catch. So your idea is to drop a Makefile in scripts/livepatch
> with a check target that runs shellcheck and then a klp-build specific
> check for any non-grep0 grep? (like `grep -w 'grep' klp-build`). If
> so, any other things to should check for?
That's all I can think of, hopefully shellcheck would catch most of the
other footguns.
--
Josh
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox