From f9fb44b0ecefc1f218db56661ed66d4e8d67317d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 10 Feb 2026 13:50:09 -0800 Subject: objtool/klp: Fix detection of corrupt static branch/call entries 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 Reviewed-and-tested-by: Song Liu Link: https://patch.msgid.link/124ad747b751df0df1725eff89de8332e3fb26d6.1770759954.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/klp-diff.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') 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) { -- cgit v1.2.3 From e476bb277cf91b7ac3ea803ec78a4f0791bddec3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 10 Feb 2026 13:50:10 -0800 Subject: objtool/klp: Disable unsupported pr_debug() usage 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. Reviewed-and-tested-by: Song Liu Link: https://patch.msgid.link/3a7db3a5b7d4abf9b2534803a74e2e7231322738.1770759954.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/klp-diff.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'tools') 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; -- cgit v1.2.3 From 11c2adcd1fa2a9380a507db1e57c8542bfc81827 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 10 Feb 2026 13:50:11 -0800 Subject: objtool/klp: Avoid NULL pointer dereference when printing code symbol name Fix a hypothetical NULL pointer defereference of the 'code_sym' variable. In theory this should never happen. Reviewed-and-tested-by: Song Liu Link: https://patch.msgid.link/64116517bc93851a98fe366ea0a4d807f4c70aab.1770759954.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/klp-diff.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'tools') 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 = ""; + 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; } -- cgit v1.2.3 From 32234049107d012703d50547e815f198f147968b Mon Sep 17 00:00:00 2001 From: HONG Yifan Date: Tue, 3 Mar 2026 01:03:39 +0000 Subject: objtool: Use HOSTCFLAGS for HAVE_XXHASH test Previously, HAVE_XXHASH is tested by invoking HOSTCC without HOSTCFLAGS. Consider the following scenario: - The host machine has libxxhash installed - We build the kernel with HOSTCFLAGS containing a --sysroot that does not have xxhash.h (for hermetic builds) In this case, HAVE_XXHASH is set to y, but when it builds objtool with HOSTCFLAGS, because the --sysroot does not contain xxhash.h, the following error is raised: <...>/common/tools/objtool/include/objtool/checksum_types.h:12:10: fatal error: 'xxhash.h' file not found 12 | #include | ^~~~~~~~~~ To resolve the error, we test HAVE_XXHASH by invoking HOSTCC with HOSTCFLAGS. Signed-off-by: HONG Yifan Reviewed-by: Carlos Llamas Link: https://patch.msgid.link/20260303010340.306164-1-elsk@google.com Signed-off-by: Josh Poimboeuf --- tools/objtool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 6964175abdfd..b8b8529f8ea9 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -13,7 +13,7 @@ endif ifeq ($(ARCH_HAS_KLP),y) HAVE_XXHASH = $(shell printf "$(pound)include \nXXH3_state_t *state;int main() {}" | \ - $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n) + $(HOSTCC) $(HOSTCFLAGS) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n) ifeq ($(HAVE_XXHASH),y) BUILD_KLP := y LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \ -- cgit v1.2.3 From 356e4b2f5b80f757965f3f4d0219c81fca91b6f2 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 4 Mar 2026 19:31:20 -0800 Subject: objtool: Fix data alignment in elf_add_data() Any data added to a section needs to be aligned in accordance with the section's sh_addralign value. Particularly strings added to a .str1.8 section. Otherwise you may get some funky strings. Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files") Link: https://patch.msgid.link/d962fc0ca24fa0825cca8dad71932dccdd9312a9.1772681234.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 2c02c7b49265..3da90686350d 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_addralign; offset = ALIGN(sec->sh.sh_size, sec->sh.sh_addralign); sec->sh.sh_size = offset + size; -- cgit v1.2.3 From 1fd1dc41724319406b0aff221a352a400b0ddfc5 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 4 Mar 2026 19:31:21 -0800 Subject: objtool: Fix ERROR_INSN() error message Confusingly, ERROR_INSN() shows "warning:" instead of "error:". Fix that. Link: https://patch.msgid.link/c4fe793bb3d23fac2c636b2511059af1158410e2.1772681234.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/include/objtool/warn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index 2b27b54096b8..fa8b7d292e83 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -107,7 +107,7 @@ static inline char *offstr(struct section *sec, unsigned long offset) #define ERROR_ELF(format, ...) __WARN_ELF(ERROR_STR, format, ##__VA_ARGS__) #define ERROR_GLIBC(format, ...) __WARN_GLIBC(ERROR_STR, format, ##__VA_ARGS__) #define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__) -#define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__) +#define ERROR_INSN(insn, format, ...) ERROR_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__) extern bool debug; extern int indent; -- cgit v1.2.3 From 7fdaa640c810cb42090a182c33f905bcc47a616a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 6 Mar 2026 09:35:06 -0800 Subject: objtool: Handle Clang RSP musical chairs For no apparent reason (possibly related to CONFIG_KMSAN), Clang can randomly pass the value of RSP to other registers and then back again to RSP. Handle that accordingly. Fixes the following warnings: drivers/input/misc/uinput.o: warning: objtool: uinput_str_to_user+0x165: undefined stack state drivers/input/misc/uinput.o: warning: objtool: uinput_str_to_user+0x165: unknown CFA base reg -1 Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/90956545-2066-46e3-b547-10c884582eb0@app.fastmail.com Link: https://patch.msgid.link/240e6a172cc73292499334a3724d02ccb3247fc7.1772818491.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/arch/x86/decode.c | 62 +++++++++++++++-------------------------- tools/objtool/check.c | 14 ++++++++++ 2 files changed, 37 insertions(+), 39 deletions(-) (limited to 'tools') diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 73bfea220d1b..c5817829cdfa 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -395,52 +395,36 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (!rex_w) break; - if (modrm_reg == CFI_SP) { - - if (mod_is_reg()) { - /* mov %rsp, reg */ - ADD_OP(op) { - op->src.type = OP_SRC_REG; - op->src.reg = CFI_SP; - op->dest.type = OP_DEST_REG; - op->dest.reg = modrm_rm; - } - break; - - } else { - /* skip RIP relative displacement */ - if (is_RIP()) - break; - - /* skip nontrivial SIB */ - if (have_SIB()) { - modrm_rm = sib_base; - if (sib_index != CFI_SP) - break; - } - - /* mov %rsp, disp(%reg) */ - ADD_OP(op) { - op->src.type = OP_SRC_REG; - op->src.reg = CFI_SP; - op->dest.type = OP_DEST_REG_INDIRECT; - op->dest.reg = modrm_rm; - op->dest.offset = ins.displacement.value; - } - break; + if (mod_is_reg()) { + /* mov reg, reg */ + ADD_OP(op) { + op->src.type = OP_SRC_REG; + op->src.reg = modrm_reg; + op->dest.type = OP_DEST_REG; + op->dest.reg = modrm_rm; } - break; } - if (rm_is_reg(CFI_SP)) { + /* skip RIP relative displacement */ + if (is_RIP()) + break; - /* mov reg, %rsp */ + /* skip nontrivial SIB */ + if (have_SIB()) { + modrm_rm = sib_base; + if (sib_index != CFI_SP) + break; + } + + /* mov %rsp, disp(%reg) */ + if (modrm_reg == CFI_SP) { ADD_OP(op) { op->src.type = OP_SRC_REG; - op->src.reg = modrm_reg; - op->dest.type = OP_DEST_REG; - op->dest.reg = CFI_SP; + op->src.reg = CFI_SP; + op->dest.type = OP_DEST_REG_INDIRECT; + op->dest.reg = modrm_rm; + op->dest.offset = ins.displacement.value; } break; } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index a30379e4ff97..786b2f2adbab 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3000,6 +3000,20 @@ static int update_cfi_state(struct instruction *insn, cfi->stack_size += 8; } + else if (cfi->vals[op->src.reg].base == CFI_CFA) { + /* + * Clang RSP musical chairs: + * + * mov %rsp, %rdx [handled above] + * ... + * mov %rdx, %rbx [handled here] + * ... + * mov %rbx, %rsp [handled above] + */ + cfi->vals[op->dest.reg].base = CFI_CFA; + cfi->vals[op->dest.reg].offset = cfi->vals[op->src.reg].offset; + } + break; -- cgit v1.2.3 From 9a73f085dc91980ab7fcc5e9716f4449424b3b59 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 6 Mar 2026 10:28:14 -0800 Subject: objtool: Fix another stack overflow in validate_branch() The insn state is getting saved on the stack twice for each recursive iteration. No need for that, once is enough. Fixes the following reported stack overflow: drivers/scsi/qla2xxx/qla_dbg.o: error: SIGSEGV: objtool stack overflow! Segmentation fault Fixes: 70589843b36f ("objtool: Add option to trace function validation") Reported-by: Arnd Bergmann Closes: https://lore.kernel.org/90956545-2066-46e3-b547-10c884582eb0@app.fastmail.com Link: https://patch.msgid.link/8b97f62d083457f3b0a29a424275f7957dd3372f.1772821683.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 786b2f2adbab..91b3ff4803cf 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3748,7 +3748,7 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func, static int validate_branch(struct objtool_file *file, struct symbol *func, struct instruction *insn, struct insn_state state); static int do_validate_branch(struct objtool_file *file, struct symbol *func, - struct instruction *insn, struct insn_state state); + struct instruction *insn, struct insn_state *state); static int validate_insn(struct objtool_file *file, struct symbol *func, struct instruction *insn, struct insn_state *statep, @@ -4013,7 +4013,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func, * tools/objtool/Documentation/objtool.txt. */ static int do_validate_branch(struct objtool_file *file, struct symbol *func, - struct instruction *insn, struct insn_state state) + struct instruction *insn, struct insn_state *state) { struct instruction *next_insn, *prev_insn = NULL; bool dead_end; @@ -4044,7 +4044,7 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func, return 1; } - ret = validate_insn(file, func, insn, &state, prev_insn, next_insn, + ret = validate_insn(file, func, insn, state, prev_insn, next_insn, &dead_end); if (!insn->trace) { @@ -4055,7 +4055,7 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func, } if (!dead_end && !next_insn) { - if (state.cfi.cfa.base == CFI_UNDEFINED) + if (state->cfi.cfa.base == CFI_UNDEFINED) return 0; if (file->ignore_unreachables) return 0; @@ -4080,7 +4080,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, int ret; trace_depth_inc(); - ret = do_validate_branch(file, func, insn, state); + ret = do_validate_branch(file, func, insn, &state); trace_depth_dec(); return ret; -- cgit v1.2.3