Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c17909a authored by Sven Schnelle's avatar Sven Schnelle Committed by Sasha Levin
Browse files

s390/ptrace: fix setting syscall number



[ 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: default avatarSven Schnelle <svens@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 64f7b10a
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -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.
@@ -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
		 */
@@ -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)) {
		/*
@@ -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;
		}