s390/ptrace: fix setting syscall number
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>
This commit is contained in:
Родитель
00332c16b1
Коммит
873e5a763d
|
@ -323,6 +323,25 @@ static inline void __poke_user_per(struct task_struct *child,
|
||||||
child->thread.per_user.end = data;
|
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
|
* Write a word to the user area of a process at location addr. This
|
||||||
* operation does have an additional problem compared to peek_user.
|
* operation does have an additional problem compared to peek_user.
|
||||||
|
@ -334,7 +353,9 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||||
struct user *dummy = NULL;
|
struct user *dummy = NULL;
|
||||||
addr_t offset;
|
addr_t offset;
|
||||||
|
|
||||||
|
|
||||||
if (addr < (addr_t) &dummy->regs.acrs) {
|
if (addr < (addr_t) &dummy->regs.acrs) {
|
||||||
|
struct pt_regs *regs = task_pt_regs(child);
|
||||||
/*
|
/*
|
||||||
* psw and gprs are stored on the stack
|
* psw and gprs are stored on the stack
|
||||||
*/
|
*/
|
||||||
|
@ -352,7 +373,11 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
|
||||||
/* Invalid addressing mode bits */
|
/* Invalid addressing mode bits */
|
||||||
return -EINVAL;
|
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)) {
|
} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
|
||||||
/*
|
/*
|
||||||
|
@ -718,6 +743,10 @@ static int __poke_user_compat(struct task_struct *child,
|
||||||
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
|
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
|
||||||
(__u64)(tmp & PSW32_ADDR_AMODE);
|
(__u64)(tmp & PSW32_ADDR_AMODE);
|
||||||
} else {
|
} 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 */
|
/* gpr 0-15 */
|
||||||
*(__u32*)((addr_t) ®s->psw + addr*2 + 4) = tmp;
|
*(__u32*)((addr_t) ®s->psw + addr*2 + 4) = tmp;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче