Commit 88b60426 authored by Vasily Gorbik's avatar Vasily Gorbik Committed by Heiko Carstens
Browse files

s390/unwind: use current_frame_address() to unwind current task



current_stack_pointer() simply returns current value of %r15. If
current_stack_pointer() caller allocates stack (which is the case in
unwind code) %r15 points to a stack frame allocated for callees, meaning
current_stack_pointer() caller (e.g. stack_trace_save) will end up in
the stacktrace. This is not expected by stack_trace_save*() callers and
causes problems.

current_frame_address() on the other hand returns function stack frame
address, which matches %r15 upon function invocation. Using it in
get_stack_pointer() makes it more aligned with x86 implementation
(according to BACKTRACE_SELF_TEST output) and meets stack_trace_save*()
caller's expectations, notably KCSAN.

Also make sure unwind_start is always inlined.

Reported-by: default avatarNathan Chancellor <nathan@kernel.org>
Suggested-by: default avatarMarco Elver <elver@google.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
Tested-by: default avatarMarco Elver <elver@google.com>
Tested-by: default avatarNathan Chancellor <nathan@kernel.org>
Link: https://lore.kernel.org/r/patch.git-04dd26be3043.your-ad-here.call-01630504868-ext-6188@work.hours


Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 81912856
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -34,16 +34,6 @@ static inline bool on_stack(struct stack_info *info,
	return addr >= info->begin && addr + len <= info->end;
}

static __always_inline unsigned long get_stack_pointer(struct task_struct *task,
						       struct pt_regs *regs)
{
	if (regs)
		return (unsigned long) kernel_stack_pointer(regs);
	if (task == current)
		return current_stack_pointer();
	return (unsigned long) task->thread.ksp;
}

/*
 * Stack layout of a C stack frame.
 */
@@ -74,6 +64,16 @@ struct stack_frame {
	((unsigned long)__builtin_frame_address(0) -			\
	 offsetof(struct stack_frame, back_chain))

static __always_inline unsigned long get_stack_pointer(struct task_struct *task,
						       struct pt_regs *regs)
{
	if (regs)
		return (unsigned long)kernel_stack_pointer(regs);
	if (task == current)
		return current_frame_address();
	return (unsigned long)task->thread.ksp;
}

/*
 * To keep this simple mark register 2-6 as being changed (volatile)
 * by the called function, even though register 6 is saved/nonvolatile.
+4 −4
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ static inline bool unwind_error(struct unwind_state *state)
	return state->error;
}

static inline void unwind_start(struct unwind_state *state,
static __always_inline void unwind_start(struct unwind_state *state,
					 struct task_struct *task,
					 struct pt_regs *regs,
					 unsigned long first_frame)