Enable unwinding of user space using back chain on s390. Based on arch_stack_walk_user_common() in arch/s390/kernel/stacktrace.c. Signed-off-by: Jens Remus <jremus@xxxxxxxxxxxxx> --- arch/s390/Kconfig | 1 + arch/s390/include/asm/unwind_user_backchain.h | 127 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 arch/s390/include/asm/unwind_user_backchain.h diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 8b29a8f0f9c3..49f231123040 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -239,6 +239,7 @@ config S390 select HAVE_SETUP_PER_CPU_AREA select HAVE_SOFTIRQ_ON_OWN_STACK select HAVE_SYSCALL_TRACEPOINTS + select HAVE_UNWIND_USER_BACKCHAIN select HAVE_UNWIND_USER_LOC_REG select HAVE_UNWIND_USER_SFRAME select HAVE_USER_RA_REG diff --git a/arch/s390/include/asm/unwind_user_backchain.h b/arch/s390/include/asm/unwind_user_backchain.h new file mode 100644 index 000000000000..ceb56b9d8411 --- /dev/null +++ b/arch/s390/include/asm/unwind_user_backchain.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_UNWIND_USER_BACKCHAIN_H +#define _ASM_S390_UNWIND_USER_BACKCHAIN_H + +#include <linux/security.h> +#ifndef ASM_OFFSETS_C +#include <asm/asm-offsets.h> +#endif + +struct stack_frame_user { + unsigned long backchain; + unsigned long unused; + /* Argument register save area. */ + unsigned long r2; + unsigned long r3; + unsigned long r4; + unsigned long r5; + unsigned long r6; + /* Other register save area. */ + unsigned long r7; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r11; + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + /* FP argument register save area. */ + unsigned long f0; + unsigned long f2; + unsigned long f4; + unsigned long f6; +}; + +struct stack_frame_vdso_wrapper { + struct stack_frame_user sf; + unsigned long return_address; +}; + +/** + * ip_invalid - Perform some basic checks whether an instruction pointer (IP) + * taken from an unreliable source is invalid + * @ip: The instruction pointer to be validated + * + * returns whether the instruction pointer is invalid + */ +static inline bool ip_invalid(unsigned long ip) +{ + if (ip & 1) + return true; + if (ip < mmap_min_addr) + return true; + if (ip >= current->mm->context.asce_limit) + return true; + return false; +} + +/** + * ip_within_vdso - Check whether an instruction pointer (IP) is within vDSO + * @ip: The instruction pointer + * + * returns whether the instruction pointer is within vDSO + */ +static inline bool ip_within_vdso(unsigned long ip) +{ + return in_range(ip, current->mm->context.vdso_base, vdso_text_size()); +} + +/** + * arch_unwind_user_backchain_next - Unwind one frame using backchain + * @state: The unwind user state + * + * returns zero when successful, otherwise -EINVAL. + */ +static inline int arch_unwind_user_backchain_next(struct unwind_user_state *state) +{ + struct stack_frame_user __user *sf; + unsigned long sp, ra; + + sf = (void __user *)state->sp; + if (__get_user(sp, (unsigned long __user *)&sf->backchain)) + return -EINVAL; + + /* + * vDSO entry code on s390 has a non-standard stack frame layout. + * See vDSO user wrapper code for details. + */ + if (!sp && ip_within_vdso(state->ip)) { + struct stack_frame_vdso_wrapper *sf_vdso = (void __user *)sf; + + if (__get_user(ra, (unsigned long __user *)&sf_vdso->return_address)) + return -EINVAL; + sf = (void __user *)((unsigned long)sf + STACK_FRAME_VDSO_OVERHEAD); + if (__get_user(sp, (unsigned long __user *)&sf->backchain)) + return -EINVAL; + } else { + sf = (void __user *)sp; + if (__get_user(ra, (unsigned long __user *)&sf->r14)) + return -EINVAL; + } + + /* ABI requires SP to be 8-byte aligned. */ + if (sp & 0x7) + return -EINVAL; + + /* + * If the IP is invalid and this is the topmost frame, + * assume the RA register has not been saved yet. + */ + if (ip_invalid(ra)) { + if (!state->topmost || !IS_ENABLED(CONFIG_HAVE_USER_RA_REG)) + return -EINVAL; + + ra = user_return_address(task_pt_regs(current)); + if (ip_invalid(ra)) + return -EINVAL; + } + + state->sp = sp; + state->ip = ra; + state->fp = 0; + + return 0; +} + +#endif /* _ASM_S390_UNWIND_USER_BACKCHAIN_H */ -- 2.48.1