From a1d863ac3e1085e1fea9caafd87252d08731de2e Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 2 Oct 2019 13:29:57 +0200 Subject: 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: 78c98f907413 ("s390/unwind: introduce stack unwind API") Signed-off-by: Ilya Leoshkevich Cc: stable@vger.kernel.org # v5.2+ Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/kernel/unwind_bc.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index 8fc9daae47a2..a8204f952315 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -46,10 +46,15 @@ bool unwind_next_frame(struct unwind_state *state) regs = state->regs; if (unlikely(regs)) { - sp = READ_ONCE_NOCHECK(regs->gprs[15]); - if (unlikely(outside_of_stack(state, sp))) { - if (!update_stack_info(state, sp)) - goto out_err; + 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]); @@ -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); -- cgit v1.2.3-59-g8ed1b From c2f2093e149d3109f1457deac4909191d9aca323 Mon Sep 17 00:00:00 2001 From: Miroslav Benes Date: Tue, 29 Oct 2019 15:39:02 +0100 Subject: s390/unwind: drop unnecessary code around calling ftrace_graph_ret_addr() The current code around calling ftrace_graph_ret_addr() is ifdeffed and also tests if ftrace redirection is present on stack. ftrace_graph_ret_addr() however performs the test internally and there is a version for !CONFIG_FUNCTION_GRAPH_TRACER as well. The unnecessary code can thus be dropped. Link: http://lkml.kernel.org/r/20191029143904.24051-2-mbenes@suse.cz Signed-off-by: Miroslav Benes Signed-off-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/kernel/unwind_bc.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index 8fc9daae47a2..5699e820c621 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -80,12 +80,7 @@ bool unwind_next_frame(struct unwind_state *state) } } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - /* Decode any ftrace redirection */ - if (ip == (unsigned long) return_to_handler) - ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, - ip, (void *) sp); -#endif + ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp); /* Update unwind state */ state->sp = sp; @@ -140,12 +135,7 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, reliable = false; } -#ifdef CONFIG_FUNCTION_GRAPH_TRACER - /* Decode any ftrace redirection */ - if (ip == (unsigned long) return_to_handler) - ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, - ip, NULL); -#endif + ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL); /* Update unwind state */ state->sp = sp; -- cgit v1.2.3-59-g8ed1b From 67f5593419878798bb306632cdca0698a2dd3cbd Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 22 Nov 2019 15:53:30 +0100 Subject: s390/unwind: report an error if pt_regs are not on stack If unwinder is looking at pt_regs which is not on stack then something went wrong and an error has to be reported rather than successful unwinding termination. Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/kernel/unwind_bc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index fa111d3d378f..fd90b6e21663 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -76,7 +76,7 @@ bool unwind_next_frame(struct unwind_state *state) /* No back-chain, look for a pt_regs structure */ sp = state->sp + STACK_FRAME_OVERHEAD; if (!on_stack(info, sp, sizeof(struct pt_regs))) - goto out_stop; + goto out_err; regs = (struct pt_regs *) sp; if (READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE) goto out_stop; -- cgit v1.2.3-59-g8ed1b From 97806dfb6f3838ee4b7bc69e6f160d83eadbc74a Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 22 Nov 2019 15:58:42 +0100 Subject: s390/unwind: make reuse_sp default when unwinding pt_regs Currently unwinder yields 2 entries when pt_regs are met: sp="address of pt_regs itself" ip=pt_regs->psw sp=pt_regs->gprs[15] ip="r14 from stack frame pointed by pt_regs->gprs[15]" And neither of those 2 states (combination of sp and ip) ever happened. reuse_sp has been introduced by commit a1d863ac3e10 ("s390/unwind: fix mixing regs and sp"). reuse_sp=true makes unwinder keen to produce the following result, when pt_regs are given (as an arg to unwind_start): sp=pt_regs->gprs[15] ip=pt_regs->psw sp=pt_regs->gprs[15] ip="r14 from stack frame pointed by pt_regs->gprs[15]" The first state is an actual state in which a task was when pt_regs were collected. The second state is marked unreliable and is for debugging purposes to cover the case when a task has been interrupted in between stack frame allocation and writing back_chain - in this case r14 might show an actual caller. Make unwinder behaviour enabled via reuse_sp=true default and drop the special case handling. Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/unwind.h | 1 - arch/s390/kernel/unwind_bc.c | 21 +++++++-------------- 2 files changed, 7 insertions(+), 15 deletions(-) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/include/asm/unwind.h b/arch/s390/include/asm/unwind.h index a2d8dd766987..5d6c8fe7a271 100644 --- a/arch/s390/include/asm/unwind.h +++ b/arch/s390/include/asm/unwind.h @@ -35,7 +35,6 @@ 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; diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index fd90b6e21663..ac6cfab567d1 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -46,16 +46,7 @@ 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; - } - } + sp = state->sp; sf = (struct stack_frame *) sp; ip = READ_ONCE_NOCHECK(sf->gprs[8]); reliable = false; @@ -81,6 +72,11 @@ bool unwind_next_frame(struct unwind_state *state) if (READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE) goto out_stop; ip = READ_ONCE_NOCHECK(regs->psw.addr); + sp = READ_ONCE_NOCHECK(regs->gprs[15]); + if (unlikely(outside_of_stack(state, sp))) { + if (!update_stack_info(state, sp)) + goto out_err; + } reliable = true; } } @@ -107,7 +103,7 @@ 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; + bool reliable; struct stack_frame *sf; unsigned long ip; @@ -134,12 +130,10 @@ 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; } ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL); @@ -148,6 +142,5 @@ 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); -- cgit v1.2.3-59-g8ed1b From e76e69611e944ecc38aaf8fe3a7bebdc3c5daf84 Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Fri, 22 Nov 2019 16:49:13 +0100 Subject: s390/unwind: stop gracefully at task pt_regs Consider reaching task pt_regs graceful unwinder termination. Task pt_regs itself never contains a valid state to which a task might return within the kernel context (user task pt_regs is a special case). Since we already avoid printing user task pt_regs and in most cases we don't even bother filling task pt_regs psw and r15 with something reasonable simply skip task pt_regs altogether. With this change unwind_error() now accurately represent whether unwinder reached task pt_regs successfully or failed along the way. Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/kernel/unwind_bc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index ac6cfab567d1..c5ebb8a4cdd6 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -36,6 +36,12 @@ static bool update_stack_info(struct unwind_state *state, unsigned long sp) return true; } +static inline bool is_task_pt_regs(struct unwind_state *state, + struct pt_regs *regs) +{ + return task_pt_regs(state->task) == regs; +} + bool unwind_next_frame(struct unwind_state *state) { struct stack_info *info = &state->stack_info; @@ -69,7 +75,7 @@ bool unwind_next_frame(struct unwind_state *state) if (!on_stack(info, sp, sizeof(struct pt_regs))) goto out_err; regs = (struct pt_regs *) sp; - if (READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE) + if (is_task_pt_regs(state, regs)) goto out_stop; ip = READ_ONCE_NOCHECK(regs->psw.addr); sp = READ_ONCE_NOCHECK(regs->gprs[15]); -- cgit v1.2.3-59-g8ed1b From 222ee9087a730b1df08d09baed0d03626e67600f Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Wed, 27 Nov 2019 17:37:51 +0100 Subject: s390/unwind: start unwinding from reliable state A comment in arch/s390/include/asm/unwind.h says: > If 'first_frame' is not zero unwind_start skips unwind frames until it > reaches the specified stack pointer. > The end of the unwinding is indicated with unwind_done, this can be true > right after unwind_start, e.g. with first_frame!=0 that can not be found. > unwind_next_frame skips to the next frame. > Once the unwind is completed unwind_error() can be used to check if there > has been a situation where the unwinder could not correctly understand > the tasks call chain. With this change backchain unwinder now comply with behaviour described. As well as matches orc unwinder implementation. Now unwinder starts from reliable state, i.e. __unwind_start own stack frame is taken or stack frame generated by __switch_to (ksp) - both known to be valid. In case of pt_regs %r15 is better match for pt_regs psw, than sometimes random "sp" caller passed. Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/include/asm/unwind.h | 6 +++--- arch/s390/kernel/unwind_bc.c | 42 ++++++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 17 deletions(-) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/include/asm/unwind.h b/arch/s390/include/asm/unwind.h index 5d6c8fe7a271..de9006b0cfeb 100644 --- a/arch/s390/include/asm/unwind.h +++ b/arch/s390/include/asm/unwind.h @@ -58,11 +58,11 @@ static inline bool unwind_error(struct unwind_state *state) static inline void unwind_start(struct unwind_state *state, struct task_struct *task, struct pt_regs *regs, - unsigned long sp) + unsigned long first_frame) { task = task ?: current; - sp = sp ?: get_stack_pointer(task, regs); - __unwind_start(state, task, regs, sp); + first_frame = first_frame ?: get_stack_pointer(task, regs); + __unwind_start(state, task, regs, first_frame); } static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state) diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index c5ebb8a4cdd6..e1371cdf9fa5 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -105,13 +105,11 @@ out_stop: EXPORT_SYMBOL_GPL(unwind_next_frame); void __unwind_start(struct unwind_state *state, struct task_struct *task, - struct pt_regs *regs, unsigned long sp) + struct pt_regs *regs, unsigned long first_frame) { struct stack_info *info = &state->stack_info; - unsigned long *mask = &state->stack_mask; - bool reliable; struct stack_frame *sf; - unsigned long ip; + unsigned long ip, sp; memset(state, 0, sizeof(*state)); state->task = task; @@ -123,23 +121,28 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, return; } + /* Get the instruction pointer from pt_regs or the stack frame */ + if (regs) { + ip = regs->psw.addr; + sp = regs->gprs[15]; + } else if (task == current) { + sp = current_frame_address(); + } else { + sp = task->thread.ksp; + } + /* Get current stack pointer and initialize stack info */ - if (get_stack_info(sp, task, info, mask) != 0 || - !on_stack(info, sp, sizeof(struct stack_frame))) { + if (!update_stack_info(state, sp)) { /* Something is wrong with the stack pointer */ info->type = STACK_TYPE_UNKNOWN; state->error = true; return; } - /* Get the instruction pointer from pt_regs or the stack frame */ - if (regs) { - ip = READ_ONCE_NOCHECK(regs->psw.addr); - reliable = true; - } else { - sf = (struct stack_frame *) sp; + if (!regs) { + /* Stack frame is within valid stack */ + sf = (struct stack_frame *)sp; ip = READ_ONCE_NOCHECK(sf->gprs[8]); - reliable = false; } ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL); @@ -147,6 +150,17 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task, /* Update unwind state */ state->sp = sp; state->ip = ip; - state->reliable = reliable; + state->reliable = true; + + if (!first_frame) + return; + /* Skip through the call chain to the specified starting frame */ + while (!unwind_done(state)) { + if (on_stack(&state->stack_info, first_frame, sizeof(struct stack_frame))) { + if (state->sp >= first_frame) + break; + } + unwind_next_frame(state); + } } EXPORT_SYMBOL_GPL(__unwind_start); -- cgit v1.2.3-59-g8ed1b From bf018ee644897d7982e1b8dd8b15e97db6e1a4da Mon Sep 17 00:00:00 2001 From: Vasily Gorbik Date: Wed, 27 Nov 2019 18:12:04 +0100 Subject: s390/unwind: filter out unreliable bogus %r14 Currently unwinder unconditionally returns %r14 from the first frame pointed by %r15 from pt_regs. A task could be interrupted when a function already allocated this frame (if it needs it) for its callees or to store local variables. In that case this frame would contain random values from stack or values stored there by a callee. As we are only interested in %r14 to get potential return address, skip bogus return addresses which doesn't belong to kernel text. This helps to avoid duplicating filtering logic in unwider users, most of which use unwind_get_return_address() and would choke on bogus 0 address returned by it otherwise. Reviewed-by: Heiko Carstens Signed-off-by: Vasily Gorbik --- arch/s390/kernel/unwind_bc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index e1371cdf9fa5..ef42d5f77ce7 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -57,6 +57,11 @@ bool unwind_next_frame(struct unwind_state *state) ip = READ_ONCE_NOCHECK(sf->gprs[8]); reliable = false; regs = NULL; + if (!__kernel_text_address(ip)) { + /* skip bogus %r14 */ + state->regs = NULL; + return unwind_next_frame(state); + } } else { sf = (struct stack_frame *) state->sp; sp = READ_ONCE_NOCHECK(sf->back_chain); -- cgit v1.2.3-59-g8ed1b From be2d11b2a1e86586ace9f6839a159b170b00f2b3 Mon Sep 17 00:00:00 2001 From: Miroslav Benes Date: Wed, 27 Nov 2019 19:35:19 +0100 Subject: s390/unwind: add stack pointer alignment sanity checks ABI requires SP to be aligned 8 bytes, report unwinding error otherwise. Link: https://lkml.kernel.org/r/20191106095601.29986-5-mbenes@suse.cz Reviewed-by: Heiko Carstens Tested-by: Miroslav Benes Signed-off-by: Miroslav Benes Signed-off-by: Vasily Gorbik --- arch/s390/kernel/dumpstack.c | 4 ++++ arch/s390/kernel/unwind_bc.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'arch/s390/kernel/unwind_bc.c') diff --git a/arch/s390/kernel/dumpstack.c b/arch/s390/kernel/dumpstack.c index d74e21a23703..d306fe04489a 100644 --- a/arch/s390/kernel/dumpstack.c +++ b/arch/s390/kernel/dumpstack.c @@ -94,6 +94,10 @@ int get_stack_info(unsigned long sp, struct task_struct *task, if (!sp) goto unknown; + /* Sanity check: ABI requires SP to be aligned 8 bytes. */ + if (sp & 0x7) + goto unknown; + /* Check per-task stack */ if (in_task_stack(sp, task, info)) goto recursion_check; diff --git a/arch/s390/kernel/unwind_bc.c b/arch/s390/kernel/unwind_bc.c index ef42d5f77ce7..da2d4d4c5b0e 100644 --- a/arch/s390/kernel/unwind_bc.c +++ b/arch/s390/kernel/unwind_bc.c @@ -92,6 +92,10 @@ bool unwind_next_frame(struct unwind_state *state) } } + /* Sanity check: ABI requires SP to be aligned 8 bytes. */ + if (sp & 0x7) + goto out_err; + ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp); /* Update unwind state */ -- cgit v1.2.3-59-g8ed1b