| From e0490cf821baf685be8e9cdb75898a1c3b072d4c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 9 Mar 2020 16:44:50 +0100 |
| Subject: s390/ptrace: fix setting syscall number |
| |
| From: Sven Schnelle <svens@linux.ibm.com> |
| |
| [ Upstream commit 873e5a763d604c32988c4a78913a8dab3862d2f9 ] |
| |
| When strace wants to update the syscall number, it sets GPR2 |
| to the desired number and updates the GPR via PTRACE_SETREGSET. |
| It doesn't update regs->int_code which would cause the old syscall |
| executed on syscall restart. As we cannot change the ptrace ABI and |
| don't have a field for the interruption code, check whether the tracee |
| is in a syscall and the last instruction was svc. In that case assume |
| that the tracer wants to update the syscall number and copy the GPR2 |
| value to regs->int_code. |
| |
| Signed-off-by: Sven Schnelle <svens@linux.ibm.com> |
| Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/s390/kernel/ptrace.c | 31 ++++++++++++++++++++++++++++++- |
| 1 file changed, 30 insertions(+), 1 deletion(-) |
| |
| diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c |
| index fc2974b929c37..ee757d6f585e6 100644 |
| --- a/arch/s390/kernel/ptrace.c |
| +++ b/arch/s390/kernel/ptrace.c |
| @@ -308,6 +308,25 @@ static inline void __poke_user_per(struct task_struct *child, |
| child->thread.per_user.end = data; |
| } |
| |
| +static void fixup_int_code(struct task_struct *child, addr_t data) |
| +{ |
| + struct pt_regs *regs = task_pt_regs(child); |
| + int ilc = regs->int_code >> 16; |
| + u16 insn; |
| + |
| + if (ilc > 6) |
| + return; |
| + |
| + if (ptrace_access_vm(child, regs->psw.addr - (regs->int_code >> 16), |
| + &insn, sizeof(insn), FOLL_FORCE) != sizeof(insn)) |
| + return; |
| + |
| + /* double check that tracee stopped on svc instruction */ |
| + if ((insn >> 8) != 0xa) |
| + return; |
| + |
| + regs->int_code = 0x20000 | (data & 0xffff); |
| +} |
| /* |
| * Write a word to the user area of a process at location addr. This |
| * operation does have an additional problem compared to peek_user. |
| @@ -319,7 +338,9 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) |
| struct user *dummy = NULL; |
| addr_t offset; |
| |
| + |
| if (addr < (addr_t) &dummy->regs.acrs) { |
| + struct pt_regs *regs = task_pt_regs(child); |
| /* |
| * psw and gprs are stored on the stack |
| */ |
| @@ -337,7 +358,11 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) |
| /* Invalid addressing mode bits */ |
| return -EINVAL; |
| } |
| - *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data; |
| + |
| + if (test_pt_regs_flag(regs, PIF_SYSCALL) && |
| + addr == offsetof(struct user, regs.gprs[2])) |
| + fixup_int_code(child, data); |
| + *(addr_t *)((addr_t) ®s->psw + addr) = data; |
| |
| } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { |
| /* |
| @@ -703,6 +728,10 @@ static int __poke_user_compat(struct task_struct *child, |
| regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) | |
| (__u64)(tmp & PSW32_ADDR_AMODE); |
| } else { |
| + |
| + if (test_pt_regs_flag(regs, PIF_SYSCALL) && |
| + addr == offsetof(struct compat_user, regs.gprs[2])) |
| + fixup_int_code(child, data); |
| /* gpr 0-15 */ |
| *(__u32*)((addr_t) ®s->psw + addr*2 + 4) = tmp; |
| } |
| -- |
| 2.25.1 |
| |