mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-05-10 09:13:51 +00:00
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: 78c98f9074
("s390/unwind: introduce stack unwind API")
Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
Cc: stable@vger.kernel.org # v5.2+
Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
b8e51a6a9d
commit
a1d863ac3e
2 changed files with 14 additions and 5 deletions
|
@ -35,6 +35,7 @@ struct unwind_state {
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
struct pt_regs *regs;
|
struct pt_regs *regs;
|
||||||
unsigned long sp, ip;
|
unsigned long sp, ip;
|
||||||
|
bool reuse_sp;
|
||||||
int graph_idx;
|
int graph_idx;
|
||||||
bool reliable;
|
bool reliable;
|
||||||
bool error;
|
bool error;
|
||||||
|
|
|
@ -46,10 +46,15 @@ bool unwind_next_frame(struct unwind_state *state)
|
||||||
|
|
||||||
regs = state->regs;
|
regs = state->regs;
|
||||||
if (unlikely(regs)) {
|
if (unlikely(regs)) {
|
||||||
sp = READ_ONCE_NOCHECK(regs->gprs[15]);
|
if (state->reuse_sp) {
|
||||||
if (unlikely(outside_of_stack(state, sp))) {
|
sp = state->sp;
|
||||||
if (!update_stack_info(state, sp))
|
state->reuse_sp = false;
|
||||||
goto out_err;
|
} 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;
|
sf = (struct stack_frame *) sp;
|
||||||
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
||||||
|
@ -107,9 +112,9 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||||
{
|
{
|
||||||
struct stack_info *info = &state->stack_info;
|
struct stack_info *info = &state->stack_info;
|
||||||
unsigned long *mask = &state->stack_mask;
|
unsigned long *mask = &state->stack_mask;
|
||||||
|
bool reliable, reuse_sp;
|
||||||
struct stack_frame *sf;
|
struct stack_frame *sf;
|
||||||
unsigned long ip;
|
unsigned long ip;
|
||||||
bool reliable;
|
|
||||||
|
|
||||||
memset(state, 0, sizeof(*state));
|
memset(state, 0, sizeof(*state));
|
||||||
state->task = task;
|
state->task = task;
|
||||||
|
@ -134,10 +139,12 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||||
if (regs) {
|
if (regs) {
|
||||||
ip = READ_ONCE_NOCHECK(regs->psw.addr);
|
ip = READ_ONCE_NOCHECK(regs->psw.addr);
|
||||||
reliable = true;
|
reliable = true;
|
||||||
|
reuse_sp = true;
|
||||||
} else {
|
} else {
|
||||||
sf = (struct stack_frame *) sp;
|
sf = (struct stack_frame *) sp;
|
||||||
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
||||||
reliable = false;
|
reliable = false;
|
||||||
|
reuse_sp = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
@ -151,5 +158,6 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||||
state->sp = sp;
|
state->sp = sp;
|
||||||
state->ip = ip;
|
state->ip = ip;
|
||||||
state->reliable = reliable;
|
state->reliable = reliable;
|
||||||
|
state->reuse_sp = reuse_sp;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__unwind_start);
|
EXPORT_SYMBOL_GPL(__unwind_start);
|
||||||
|
|
Loading…
Add table
Reference in a new issue