Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff3f7e24 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Ingo Molnar
Browse files

x86/entry: Fix the end of the stack for newly forked tasks



When unwinding a task, the end of the stack is always at the same offset
right below the saved pt_regs, regardless of which syscall was used to
enter the kernel.  That convention allows the unwinder to verify that a
stack is sane.

However, newly forked tasks don't always follow that convention, as
reported by the following unwinder warning seen by Dave Jones:

  WARNING: kernel stack frame pointer at ffffc90001443f30 in kworker/u8:8:30468 has bad value           (null)

The warning was due to the following call chain:

  (ftrace handler)
  call_usermodehelper_exec_async+0x5/0x140
  ret_from_fork+0x22/0x30

The problem is that ret_from_fork() doesn't create a stack frame before
calling other functions.  Fix that by carefully using the frame pointer
macros.

In addition to conforming to the end of stack convention, this also
makes related stack traces more sensible by making it clear to the user
that ret_from_fork() was involved.

Reported-by: default avatarDave Jones <davej@codemonkey.org.uk>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Miroslav Benes <mbenes@suse.cz>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/8854cdaab980e9700a81e9ebf0d4238e4bbb68ef.1483978430.git.jpoimboe@redhat.com


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 2c96b2fe
Loading
Loading
Loading
Loading
+11 −19
Original line number Diff line number Diff line
@@ -254,23 +254,6 @@ ENTRY(__switch_to_asm)
	jmp	__switch_to
END(__switch_to_asm)

/*
 * The unwinder expects the last frame on the stack to always be at the same
 * offset from the end of the page, which allows it to validate the stack.
 * Calling schedule_tail() directly would break that convention because its an
 * asmlinkage function so its argument has to be pushed on the stack.  This
 * wrapper creates a proper "end of stack" frame header before the call.
 */
ENTRY(schedule_tail_wrapper)
	FRAME_BEGIN

	pushl	%eax
	call	schedule_tail
	popl	%eax

	FRAME_END
	ret
ENDPROC(schedule_tail_wrapper)
/*
 * A newly forked process directly context switches into this address.
 *
@@ -279,15 +262,24 @@ ENDPROC(schedule_tail_wrapper)
 * edi: kernel thread arg
 */
ENTRY(ret_from_fork)
	call	schedule_tail_wrapper
	FRAME_BEGIN		/* help unwinder find end of stack */

	/*
	 * schedule_tail() is asmlinkage so we have to put its 'prev' argument
	 * on the stack.
	 */
	pushl	%eax
	call	schedule_tail
	popl	%eax

	testl	%ebx, %ebx
	jnz	1f		/* kernel threads are uncommon */

2:
	/* When we fork, we trace the syscall return in the child, too. */
	movl    %esp, %eax
	leal	FRAME_OFFSET(%esp), %eax
	call    syscall_return_slowpath
	FRAME_END
	jmp     restore_all

	/* kernel thread */
+7 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <asm/smap.h>
#include <asm/pgtable_types.h>
#include <asm/export.h>
#include <asm/frame.h>
#include <linux/err.h>

.code64
@@ -408,6 +409,7 @@ END(__switch_to_asm)
 * r12: kernel thread arg
 */
ENTRY(ret_from_fork)
	FRAME_BEGIN			/* help unwinder find end of stack */
	movq	%rax, %rdi
	call	schedule_tail		/* rdi: 'prev' task parameter */

@@ -415,10 +417,11 @@ ENTRY(ret_from_fork)
	jnz	1f			/* kernel threads are uncommon */

2:
	movq	%rsp, %rdi
	leaq	FRAME_OFFSET(%rsp),%rdi	/* pt_regs pointer */
	call	syscall_return_slowpath	/* returns with IRQs disabled */
	TRACE_IRQS_ON			/* user mode is traced as IRQS on */
	SWAPGS
	FRAME_END
	jmp	restore_regs_and_iret

1: