Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

objtool: Fix stack overflow in validate_branch()

On an allmodconfig kernel compiled with Clang, objtool is segfaulting in
drivers/scsi/qla2xxx/qla2xxx.o due to a stack overflow in
validate_branch().

Due in part to KASAN being enabled, the qla2xxx code has a large number
of conditional jumps, causing objtool to go quite deep in its recursion.

By far the biggest offender of stack usage is the recently added
'prev_state' stack variable in validate_insn(), coming in at 328 bytes.

Move that variable (and its tracing usage) to handle_insn_ops() and make
handle_insn_ops() noinline to keep its stack frame outside the recursive
call chain.

Reported-by: Nathan Chancellor <nathan@kernel.org>
Fixes: fcb268b47a2f ("objtool: Trace instruction state changes during function validation")
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://patch.msgid.link/21bb161c23ca0d8c942a960505c0d327ca2dc7dc.1764691895.git.jpoimboe@kernel.org
Closes: https://lore.kernel.org/20251201202329.GA3225984@ax162

authored by

Josh Poimboeuf and committed by
Ingo Molnar
0c314a88 4a26e703

+13 -14
+13 -14
tools/objtool/check.c
··· 3282 3282 return 0; 3283 3283 } 3284 3284 3285 - static int handle_insn_ops(struct instruction *insn, 3286 - struct instruction *next_insn, 3287 - struct insn_state *state) 3285 + static int noinline handle_insn_ops(struct instruction *insn, 3286 + struct instruction *next_insn, 3287 + struct insn_state *state) 3288 3288 { 3289 + struct insn_state prev_state __maybe_unused = *state; 3289 3290 struct stack_op *op; 3290 - int ret; 3291 + int ret = 0; 3291 3292 3292 3293 for (op = insn->stack_ops; op; op = op->next) { 3293 3294 3294 3295 ret = update_cfi_state(insn, next_insn, &state->cfi, op); 3295 3296 if (ret) 3296 - return ret; 3297 + goto done; 3297 3298 3298 3299 if (!opts.uaccess || !insn->alt_group) 3299 3300 continue; ··· 3304 3303 state->uaccess_stack = 1; 3305 3304 } else if (state->uaccess_stack >> 31) { 3306 3305 WARN_INSN(insn, "PUSHF stack exhausted"); 3307 - return 1; 3306 + ret = 1; 3307 + goto done; 3308 3308 } 3309 3309 state->uaccess_stack <<= 1; 3310 3310 state->uaccess_stack |= state->uaccess; ··· 3321 3319 } 3322 3320 } 3323 3321 3324 - return 0; 3322 + done: 3323 + TRACE_INSN_STATE(insn, &prev_state, state); 3324 + 3325 + return ret; 3325 3326 } 3326 3327 3327 3328 static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2) ··· 3699 3694 struct instruction *prev_insn, struct instruction *next_insn, 3700 3695 bool *dead_end) 3701 3696 { 3702 - /* prev_state and alt_name are not used if there is no disassembly support */ 3703 - struct insn_state prev_state __maybe_unused; 3704 3697 char *alt_name __maybe_unused = NULL; 3705 3698 struct alternative *alt; 3706 3699 u8 visited; ··· 3801 3798 if (skip_alt_group(insn)) 3802 3799 return 0; 3803 3800 3804 - prev_state = *statep; 3805 - ret = handle_insn_ops(insn, next_insn, statep); 3806 - TRACE_INSN_STATE(insn, &prev_state, statep); 3807 - 3808 - if (ret) 3801 + if (handle_insn_ops(insn, next_insn, statep)) 3809 3802 return 1; 3810 3803 3811 3804 switch (insn->type) {