mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-26 04:16:39 +00:00
s390/ptrace: fix setting syscall number
[ Upstream commit 873e5a763d
]
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>
This commit is contained in:
parent
9cef5a5722
commit
5f311c5e64
1 changed files with 30 additions and 1 deletions
|
@ -322,6 +322,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.
|
||||
|
@ -333,7 +352,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
|
||||
*/
|
||||
|
@ -351,7 +372,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)) {
|
||||
/*
|
||||
|
@ -717,6 +742,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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue