Skip to content
This repository has been archived by the owner on Oct 25, 2022. It is now read-only.

Commit

Permalink
s390/ptrace: fix setting syscall number
Browse files Browse the repository at this point in the history
commit 873e5a763d604c32988c4a78913a8dab3862d2f9 upstream.

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 <[email protected]>
Signed-off-by: Vasily Gorbik <[email protected]>
Signed-off-by: Paul Gortmaker <[email protected]>
  • Loading branch information
svens-s390 authored and Paul Gortmaker committed Jul 16, 2020
1 parent 377141e commit 20bbcca
Showing 1 changed file with 30 additions and 1 deletion.
31 changes: 30 additions & 1 deletion arch/s390/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,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.
Expand All @@ -335,7 +354,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
*/
Expand All @@ -353,7 +374,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) &regs->psw + addr) = data;

} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
/*
Expand Down Expand Up @@ -719,6 +744,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) &regs->psw + addr*2 + 4) = tmp;
}
Expand Down

0 comments on commit 20bbcca

Please sign in to comment.