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

Commit a1d863ac authored by Ilya Leoshkevich's avatar Ilya Leoshkevich Committed by Vasily Gorbik
Browse files

s390/unwind: fix mixing regs and sp



unwind_for_each_frame stops after the first frame if regs->gprs[15] <=
sp.

The reason is that in case regs are specified, the first frame should be
regs->psw.addr and the second frame should be sp->gprs[8]. However,
currently the second frame is regs->gprs[15], which confuses
outside_of_stack().

Fix by introducing a flag to distinguish this special case from
unwinding the interrupt handler, for which the current behavior is
appropriate.

Fixes: 78c98f90 ("s390/unwind: introduce stack unwind API")
Signed-off-by: default avatarIlya Leoshkevich <iii@linux.ibm.com>
Cc: stable@vger.kernel.org # v5.2+
Reviewed-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent b8e51a6a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ struct unwind_state {
	struct task_struct *task;
	struct pt_regs *regs;
	unsigned long sp, ip;
	bool reuse_sp;
	int graph_idx;
	bool reliable;
	bool error;
+13 −5
Original line number Diff line number Diff line
@@ -46,11 +46,16 @@ bool unwind_next_frame(struct unwind_state *state)

	regs = state->regs;
	if (unlikely(regs)) {
		if (state->reuse_sp) {
			sp = state->sp;
			state->reuse_sp = false;
		} else {
			sp = READ_ONCE_NOCHECK(regs->gprs[15]);
			if (unlikely(outside_of_stack(state, sp))) {
				if (!update_stack_info(state, sp))
					goto out_err;
			}
		}
		sf = (struct stack_frame *) sp;
		ip = READ_ONCE_NOCHECK(sf->gprs[8]);
		reliable = false;
@@ -107,9 +112,9 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
{
	struct stack_info *info = &state->stack_info;
	unsigned long *mask = &state->stack_mask;
	bool reliable, reuse_sp;
	struct stack_frame *sf;
	unsigned long ip;
	bool reliable;

	memset(state, 0, sizeof(*state));
	state->task = task;
@@ -134,10 +139,12 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
	if (regs) {
		ip = READ_ONCE_NOCHECK(regs->psw.addr);
		reliable = true;
		reuse_sp = true;
	} else {
		sf = (struct stack_frame *) sp;
		ip = READ_ONCE_NOCHECK(sf->gprs[8]);
		reliable = false;
		reuse_sp = false;
	}

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
@@ -151,5 +158,6 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
	state->sp = sp;
	state->ip = ip;
	state->reliable = reliable;
	state->reuse_sp = reuse_sp;
}
EXPORT_SYMBOL_GPL(__unwind_start);