diff options
Diffstat (limited to 'arch/x86/kernel/unwind_frame.c')
| -rw-r--r-- | arch/x86/kernel/unwind_frame.c | 38 | 
1 files changed, 36 insertions, 2 deletions
| diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c index d145a0b1f529..3dc26f95d46e 100644 --- a/arch/x86/kernel/unwind_frame.c +++ b/arch/x86/kernel/unwind_frame.c @@ -44,7 +44,8 @@ static void unwind_dump(struct unwind_state *state)  			state->stack_info.type, state->stack_info.next_sp,  			state->stack_mask, state->graph_idx); -	for (sp = state->orig_sp; sp; sp = PTR_ALIGN(stack_info.next_sp, sizeof(long))) { +	for (sp = PTR_ALIGN(state->orig_sp, sizeof(long)); sp; +	     sp = PTR_ALIGN(stack_info.next_sp, sizeof(long))) {  		if (get_stack_info(sp, state->task, &stack_info, &visit_mask))  			break; @@ -174,6 +175,7 @@ static bool is_last_task_frame(struct unwind_state *state)   * This determines if the frame pointer actually contains an encoded pointer to   * pt_regs on the stack.  See ENCODE_FRAME_POINTER.   */ +#ifdef CONFIG_X86_64  static struct pt_regs *decode_frame_pointer(unsigned long *bp)  {  	unsigned long regs = (unsigned long)bp; @@ -183,6 +185,23 @@ static struct pt_regs *decode_frame_pointer(unsigned long *bp)  	return (struct pt_regs *)(regs & ~0x1);  } +#else +static struct pt_regs *decode_frame_pointer(unsigned long *bp) +{ +	unsigned long regs = (unsigned long)bp; + +	if (regs & 0x80000000) +		return NULL; + +	return (struct pt_regs *)(regs | 0x80000000); +} +#endif + +#ifdef CONFIG_X86_32 +#define KERNEL_REGS_SIZE (sizeof(struct pt_regs) - 2*sizeof(long)) +#else +#define KERNEL_REGS_SIZE (sizeof(struct pt_regs)) +#endif  static bool update_stack_state(struct unwind_state *state,  			       unsigned long *next_bp) @@ -202,7 +221,7 @@ static bool update_stack_state(struct unwind_state *state,  	regs = decode_frame_pointer(next_bp);  	if (regs) {  		frame = (unsigned long *)regs; -		len = regs_size(regs); +		len = KERNEL_REGS_SIZE;  		state->got_irq = true;  	} else {  		frame = next_bp; @@ -226,6 +245,14 @@ static bool update_stack_state(struct unwind_state *state,  	    frame < prev_frame_end)  		return false; +	/* +	 * On 32-bit with user mode regs, make sure the last two regs are safe +	 * to access: +	 */ +	if (IS_ENABLED(CONFIG_X86_32) && regs && user_mode(regs) && +	    !on_stack(info, frame, len + 2*sizeof(long))) +		return false; +  	/* Move state to the next frame: */  	if (regs) {  		state->regs = regs; @@ -328,6 +355,13 @@ bad_address:  	    state->regs->sp < (unsigned long)task_pt_regs(state->task))  		goto the_end; +	/* +	 * There are some known frame pointer issues on 32-bit.  Disable +	 * unwinder warnings on 32-bit until it gets objtool support. +	 */ +	if (IS_ENABLED(CONFIG_X86_32)) +		goto the_end; +  	if (state->regs) {  		printk_deferred_once(KERN_WARNING  			"WARNING: kernel stack regs at %p in %s:%d has bad 'bp' value %p\n", | 
