lkml.org 
[lkml]   [2020]   [Jun]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.7 321/376] sparc32: fix register window handling in genregs32_[gs]et()
    Date
    From: Al Viro <viro@zeniv.linux.org.uk>

    commit cf51e129b96847f969bfb8af1ee1516a01a70b39 upstream.

    It needs access_process_vm() if the traced process does not share
    mm with the caller. Solution is similar to what sparc64 does.
    Note that genregs32_set() is only ever called with pos being 0
    or 32 * sizeof(u32) (the latter - as part of PTRACE_SETREGS
    handling).

    Cc: stable@kernel.org
    Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    arch/sparc/kernel/ptrace_32.c | 230 ++++++++++++++++++------------------------
    1 file changed, 99 insertions(+), 131 deletions(-)

    --- a/arch/sparc/kernel/ptrace_32.c
    +++ b/arch/sparc/kernel/ptrace_32.c
    @@ -46,82 +46,79 @@ enum sparc_regset {
    REGSET_FP,
    };

    +static int regwindow32_get(struct task_struct *target,
    + const struct pt_regs *regs,
    + u32 *uregs)
    +{
    + unsigned long reg_window = regs->u_regs[UREG_I6];
    + int size = 16 * sizeof(u32);
    +
    + if (target == current) {
    + if (copy_from_user(uregs, (void __user *)reg_window, size))
    + return -EFAULT;
    + } else {
    + if (access_process_vm(target, reg_window, uregs, size,
    + FOLL_FORCE) != size)
    + return -EFAULT;
    + }
    + return 0;
    +}
    +
    +static int regwindow32_set(struct task_struct *target,
    + const struct pt_regs *regs,
    + u32 *uregs)
    +{
    + unsigned long reg_window = regs->u_regs[UREG_I6];
    + int size = 16 * sizeof(u32);
    +
    + if (target == current) {
    + if (copy_to_user((void __user *)reg_window, uregs, size))
    + return -EFAULT;
    + } else {
    + if (access_process_vm(target, reg_window, uregs, size,
    + FOLL_FORCE | FOLL_WRITE) != size)
    + return -EFAULT;
    + }
    + return 0;
    +}
    +
    static int genregs32_get(struct task_struct *target,
    const struct user_regset *regset,
    unsigned int pos, unsigned int count,
    void *kbuf, void __user *ubuf)
    {
    const struct pt_regs *regs = target->thread.kregs;
    - unsigned long __user *reg_window;
    - unsigned long *k = kbuf;
    - unsigned long __user *u = ubuf;
    - unsigned long reg;
    + u32 uregs[16];
    + int ret;

    if (target == current)
    flush_user_windows();

    - pos /= sizeof(reg);
    - count /= sizeof(reg);
    + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
    + regs->u_regs,
    + 0, 16 * sizeof(u32));
    + if (ret || !count)
    + return ret;

    - if (kbuf) {
    - for (; count > 0 && pos < 16; count--)
    - *k++ = regs->u_regs[pos++];
    -
    - reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
    - reg_window -= 16;
    - for (; count > 0 && pos < 32; count--) {
    - if (get_user(*k++, &reg_window[pos++]))
    - return -EFAULT;
    - }
    - } else {
    - for (; count > 0 && pos < 16; count--) {
    - if (put_user(regs->u_regs[pos++], u++))
    - return -EFAULT;
    - }
    -
    - reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
    - reg_window -= 16;
    - for (; count > 0 && pos < 32; count--) {
    - if (get_user(reg, &reg_window[pos++]) ||
    - put_user(reg, u++))
    - return -EFAULT;
    - }
    - }
    - while (count > 0) {
    - switch (pos) {
    - case 32: /* PSR */
    - reg = regs->psr;
    - break;
    - case 33: /* PC */
    - reg = regs->pc;
    - break;
    - case 34: /* NPC */
    - reg = regs->npc;
    - break;
    - case 35: /* Y */
    - reg = regs->y;
    - break;
    - case 36: /* WIM */
    - case 37: /* TBR */
    - reg = 0;
    - break;
    - default:
    - goto finish;
    - }
    -
    - if (kbuf)
    - *k++ = reg;
    - else if (put_user(reg, u++))
    + if (pos < 32 * sizeof(u32)) {
    + if (regwindow32_get(target, regs, uregs))
    return -EFAULT;
    - pos++;
    - count--;
    + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
    + uregs,
    + 16 * sizeof(u32), 32 * sizeof(u32));
    + if (ret || !count)
    + return ret;
    }
    -finish:
    - pos *= sizeof(reg);
    - count *= sizeof(reg);

    - return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
    - 38 * sizeof(reg), -1);
    + uregs[0] = regs->psr;
    + uregs[1] = regs->pc;
    + uregs[2] = regs->npc;
    + uregs[3] = regs->y;
    + uregs[4] = 0; /* WIM */
    + uregs[5] = 0; /* TBR */
    + return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
    + uregs,
    + 32 * sizeof(u32), 38 * sizeof(u32));
    }

    static int genregs32_set(struct task_struct *target,
    @@ -130,82 +127,53 @@ static int genregs32_set(struct task_str
    const void *kbuf, const void __user *ubuf)
    {
    struct pt_regs *regs = target->thread.kregs;
    - unsigned long __user *reg_window;
    - const unsigned long *k = kbuf;
    - const unsigned long __user *u = ubuf;
    - unsigned long reg;
    + u32 uregs[16];
    + u32 psr;
    + int ret;

    if (target == current)
    flush_user_windows();

    - pos /= sizeof(reg);
    - count /= sizeof(reg);
    -
    - if (kbuf) {
    - for (; count > 0 && pos < 16; count--)
    - regs->u_regs[pos++] = *k++;
    -
    - reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
    - reg_window -= 16;
    - for (; count > 0 && pos < 32; count--) {
    - if (put_user(*k++, &reg_window[pos++]))
    - return -EFAULT;
    - }
    - } else {
    - for (; count > 0 && pos < 16; count--) {
    - if (get_user(reg, u++))
    - return -EFAULT;
    - regs->u_regs[pos++] = reg;
    - }
    -
    - reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
    - reg_window -= 16;
    - for (; count > 0 && pos < 32; count--) {
    - if (get_user(reg, u++) ||
    - put_user(reg, &reg_window[pos++]))
    - return -EFAULT;
    - }
    - }
    - while (count > 0) {
    - unsigned long psr;
    -
    - if (kbuf)
    - reg = *k++;
    - else if (get_user(reg, u++))
    - return -EFAULT;
    -
    - switch (pos) {
    - case 32: /* PSR */
    - psr = regs->psr;
    - psr &= ~(PSR_ICC | PSR_SYSCALL);
    - psr |= (reg & (PSR_ICC | PSR_SYSCALL));
    - regs->psr = psr;
    - break;
    - case 33: /* PC */
    - regs->pc = reg;
    - break;
    - case 34: /* NPC */
    - regs->npc = reg;
    - break;
    - case 35: /* Y */
    - regs->y = reg;
    - break;
    - case 36: /* WIM */
    - case 37: /* TBR */
    - break;
    - default:
    - goto finish;
    - }
    + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    + regs->u_regs,
    + 0, 16 * sizeof(u32));
    + if (ret || !count)
    + return ret;

    - pos++;
    - count--;
    + if (pos < 32 * sizeof(u32)) {
    + if (regwindow32_get(target, regs, uregs))
    + return -EFAULT;
    + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    + uregs,
    + 16 * sizeof(u32), 32 * sizeof(u32));
    + if (ret)
    + return ret;
    + if (regwindow32_set(target, regs, uregs))
    + return -EFAULT;
    + if (!count)
    + return 0;
    }
    -finish:
    - pos *= sizeof(reg);
    - count *= sizeof(reg);
    -
    + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    + &psr,
    + 32 * sizeof(u32), 33 * sizeof(u32));
    + if (ret)
    + return ret;
    + regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
    + (psr & (PSR_ICC | PSR_SYSCALL));
    + if (!count)
    + return 0;
    + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    + &regs->pc,
    + 33 * sizeof(u32), 34 * sizeof(u32));
    + if (ret || !count)
    + return ret;
    + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
    + &regs->y,
    + 34 * sizeof(u32), 35 * sizeof(u32));
    + if (ret || !count)
    + return ret;
    return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
    - 38 * sizeof(reg), -1);
    + 35 * sizeof(u32), 38 * sizeof(u32));
    }

    static int fpregs32_get(struct task_struct *target,

    \
     
     \ /
      Last update: 2020-06-19 17:38    [W:2.315 / U:0.940 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site