Merge tag 'objtool-urgent-2026-03-15' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool fixes from Ingo Molnar:

 - Fix cross-build bug by using HOSTCFLAGS for HAVE_XXHASH test

 - Fix klp bug by fixing detection of corrupt static branch/call entries

 - Handle unsupported pr_debug() usage more gracefully

 - Fix hypothetical klp bug by avoiding NULL pointer dereference when
   printing code symbol name

 - Fix data alignment bug in elf_add_data() causing mangled strings

 - Fix confusing ERROR_INSN() error message

 - Handle unexpected Clang RSP musical chairs causing false positive
   warnings

 - Fix another objtool stack overflow in validate_branch()

* tag 'objtool-urgent-2026-03-15' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  objtool: Fix another stack overflow in validate_branch()
  objtool: Handle Clang RSP musical chairs
  objtool: Fix ERROR_INSN() error message
  objtool: Fix data alignment in elf_add_data()
  objtool: Use HOSTCFLAGS for HAVE_XXHASH test
  objtool/klp: Avoid NULL pointer dereference when printing code symbol name
  objtool/klp: Disable unsupported pr_debug() usage
  objtool/klp: Fix detection of corrupt static branch/call entries
This commit is contained in:
Linus Torvalds
2026-03-15 10:36:01 -07:00
6 changed files with 74 additions and 63 deletions

View File

@@ -13,7 +13,7 @@ endif
ifeq ($(ARCH_HAS_KLP),y)
HAVE_XXHASH = $(shell printf "$(pound)include <xxhash.h>\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) \

View File

@@ -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;
}
break;
}
if (rm_is_reg(CFI_SP)) {
/* mov reg, %rsp */
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 = CFI_SP;
op->dest.reg = modrm_rm;
}
break;
}
/* 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) */
if (modrm_reg == CFI_SP) {
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;
}

View File

@@ -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;
@@ -3734,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,
@@ -3999,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;
@@ -4030,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) {
@@ -4041,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;
@@ -4066,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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1334,25 +1334,25 @@ 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)
{
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;
@@ -1364,12 +1364,15 @@ 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) {
/* 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);
}
@@ -1392,16 +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);
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;
}
@@ -1412,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;
}