Commit afe42832 authored by Al Viro's avatar Al Viro Committed by Khalid Elmously

sparc32: fix register window handling in genregs32_[gs]et()

BugLink: https://bugs.launchpad.net/bugs/1884564

commit cf51e129 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: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
Signed-off-by: default avatarKhalid Elmously <khalid.elmously@canonical.com>
parent cf990580
...@@ -45,82 +45,79 @@ enum sparc_regset { ...@@ -45,82 +45,79 @@ enum sparc_regset {
REGSET_FP, 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, static int genregs32_get(struct task_struct *target,
const struct user_regset *regset, const struct user_regset *regset,
unsigned int pos, unsigned int count, unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf) void *kbuf, void __user *ubuf)
{ {
const struct pt_regs *regs = target->thread.kregs; const struct pt_regs *regs = target->thread.kregs;
unsigned long __user *reg_window; u32 uregs[16];
unsigned long *k = kbuf; int ret;
unsigned long __user *u = ubuf;
unsigned long reg;
if (target == current) if (target == current)
flush_user_windows(); flush_user_windows();
pos /= sizeof(reg); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
count /= sizeof(reg); regs->u_regs,
0, 16 * sizeof(u32));
if (kbuf) { if (ret || !count)
for (; count > 0 && pos < 16; count--) return ret;
*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) if (pos < 32 * sizeof(u32)) {
*k++ = reg; if (regwindow32_get(target, regs, uregs))
else if (put_user(reg, u++))
return -EFAULT; return -EFAULT;
pos++; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
count--; 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, uregs[0] = regs->psr;
38 * sizeof(reg), -1); 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, static int genregs32_set(struct task_struct *target,
...@@ -129,82 +126,53 @@ static int genregs32_set(struct task_struct *target, ...@@ -129,82 +126,53 @@ static int genregs32_set(struct task_struct *target,
const void *kbuf, const void __user *ubuf) const void *kbuf, const void __user *ubuf)
{ {
struct pt_regs *regs = target->thread.kregs; struct pt_regs *regs = target->thread.kregs;
unsigned long __user *reg_window; u32 uregs[16];
const unsigned long *k = kbuf; u32 psr;
const unsigned long __user *u = ubuf; int ret;
unsigned long reg;
if (target == current) if (target == current)
flush_user_windows(); flush_user_windows();
pos /= sizeof(reg); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
count /= sizeof(reg); regs->u_regs,
0, 16 * sizeof(u32));
if (kbuf) { if (ret || !count)
for (; count > 0 && pos < 16; count--) return ret;
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]; if (pos < 32 * sizeof(u32)) {
reg_window -= 16; if (regwindow32_get(target, regs, uregs))
for (; count > 0 && pos < 32; count--) {
if (get_user(reg, u++) ||
put_user(reg, &reg_window[pos++]))
return -EFAULT; return -EFAULT;
} ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
} uregs,
while (count > 0) { 16 * sizeof(u32), 32 * sizeof(u32));
unsigned long psr; if (ret)
return ret;
if (kbuf) if (regwindow32_set(target, regs, uregs))
reg = *k++;
else if (get_user(reg, u++))
return -EFAULT; return -EFAULT;
if (!count)
switch (pos) { return 0;
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,
pos++; &psr,
count--; 32 * sizeof(u32), 33 * sizeof(u32));
} if (ret)
finish: return ret;
pos *= sizeof(reg); regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
count *= sizeof(reg); (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, 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, static int fpregs32_get(struct task_struct *target,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment