lkml.org 
[lkml]   [2020]   [Jun]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.4 129/178] s390/ptrace: fix setting syscall number
    Date
    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 5a2b1501d9983..5aa786063eb3e 100644
    --- a/arch/s390/kernel/ptrace.c
    +++ b/arch/s390/kernel/ptrace.c
    @@ -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;
    }
    --
    2.25.1
    \
     
     \ /
      Last update: 2020-06-29 21:50    [W:4.043 / U:0.628 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site