mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
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:
@@ -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) \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user