Skip to content

Commit

Permalink
[SPARC]: Fix several regset and ptrace bugs.
Browse files Browse the repository at this point in the history
1) ptrace should pass 'current' to task_user_regset_view()

2) When fetching general registers using a 64-bit view, and
   the target is 32-bit, we have to convert.

3) Skip the whole register window get/set code block if
   the user isn't asking to access anything in there.

   Otherwise we have problems if the user doesn't have
   an address space setup.  Fetching ptrace register is
   still valid at such a time, and ptrace does not try
   to access the register window area of the regset.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Apr 10, 2008
1 parent ad4f957 commit d786a4a
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 52 deletions.
2 changes: 1 addition & 1 deletion arch/sparc/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
const struct user_regset_view *view;
int ret;

view = task_user_regset_view(child);
view = task_user_regset_view(current);

switch(request) {
case PTRACE_GETREGS: {
Expand Down
148 changes: 97 additions & 51 deletions arch/sparc64/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,85 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
preempt_enable();
}

static int get_from_target(struct task_struct *target, unsigned long uaddr,
void *kbuf, int len)
{
if (target == current) {
if (copy_from_user(kbuf, (void __user *) uaddr, len))
return -EFAULT;
} else {
int len2 = access_process_vm(target, uaddr, kbuf, len, 0);
if (len2 != len)
return -EFAULT;
}
return 0;
}

static int set_to_target(struct task_struct *target, unsigned long uaddr,
void *kbuf, int len)
{
if (target == current) {
if (copy_to_user((void __user *) uaddr, kbuf, len))
return -EFAULT;
} else {
int len2 = access_process_vm(target, uaddr, kbuf, len, 1);
if (len2 != len)
return -EFAULT;
}
return 0;
}

static int regwindow64_get(struct task_struct *target,
const struct pt_regs *regs,
struct reg_window *wbuf)
{
unsigned long rw_addr = regs->u_regs[UREG_I6];

if (test_tsk_thread_flag(current, TIF_32BIT)) {
struct reg_window32 win32;
int i;

if (get_from_target(target, rw_addr, &win32, sizeof(win32)))
return -EFAULT;
for (i = 0; i < 8; i++)
wbuf->locals[i] = win32.locals[i];
for (i = 0; i < 8; i++)
wbuf->ins[i] = win32.ins[i];
} else {
rw_addr += STACK_BIAS;
if (get_from_target(target, rw_addr, wbuf, sizeof(*wbuf)))
return -EFAULT;
}

return 0;
}

static int regwindow64_set(struct task_struct *target,
const struct pt_regs *regs,
struct reg_window *wbuf)
{
unsigned long rw_addr = regs->u_regs[UREG_I6];

if (test_tsk_thread_flag(current, TIF_32BIT)) {
struct reg_window32 win32;
int i;

for (i = 0; i < 8; i++)
win32.locals[i] = wbuf->locals[i];
for (i = 0; i < 8; i++)
win32.ins[i] = wbuf->ins[i];

if (set_to_target(target, rw_addr, &win32, sizeof(win32)))
return -EFAULT;
} else {
rw_addr += STACK_BIAS;
if (set_to_target(target, rw_addr, wbuf, sizeof(*wbuf)))
return -EFAULT;
}

return 0;
}

enum sparc_regset {
REGSET_GENERAL,
REGSET_FP,
Expand All @@ -133,25 +212,13 @@ static int genregs64_get(struct task_struct *target,
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
regs->u_regs,
0, 16 * sizeof(u64));
if (!ret) {
unsigned long __user *reg_window = (unsigned long __user *)
(regs->u_regs[UREG_I6] + STACK_BIAS);
unsigned long window[16];

if (target == current) {
if (copy_from_user(window, reg_window, sizeof(window)))
return -EFAULT;
} else {
if (access_process_vm(target,
(unsigned long) reg_window,
window,
sizeof(window), 0) !=
sizeof(window))
return -EFAULT;
}
if (!ret && count && pos < (32 * sizeof(u64))) {
struct reg_window window;

if (regwindow64_get(target, regs, &window))
return -EFAULT;
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
window,
&window,
16 * sizeof(u64),
32 * sizeof(u64));
}
Expand All @@ -173,10 +240,11 @@ static int genregs64_get(struct task_struct *target,
36 * sizeof(u64));
}

if (!ret)
if (!ret) {
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
36 * sizeof(u64), -1);

}
return ret;
}

Expand All @@ -194,42 +262,20 @@ static int genregs64_set(struct task_struct *target,
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
regs->u_regs,
0, 16 * sizeof(u64));
if (!ret && count > 0) {
unsigned long __user *reg_window = (unsigned long __user *)
(regs->u_regs[UREG_I6] + STACK_BIAS);
unsigned long window[16];
if (!ret && count && pos < (32 * sizeof(u64))) {
struct reg_window window;

if (target == current) {
if (copy_from_user(window, reg_window, sizeof(window)))
return -EFAULT;
} else {
if (access_process_vm(target,
(unsigned long) reg_window,
window,
sizeof(window), 0) !=
sizeof(window))
return -EFAULT;
}
if (regwindow64_get(target, regs, &window))
return -EFAULT;

ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
window,
&window,
16 * sizeof(u64),
32 * sizeof(u64));
if (!ret) {
if (target == current) {
if (copy_to_user(reg_window, window,
sizeof(window)))
return -EFAULT;
} else {
if (access_process_vm(target,
(unsigned long)
reg_window,
window,
sizeof(window), 1) !=
sizeof(window))
return -EFAULT;
}
}

if (!ret &&
regwindow64_set(target, regs, &window))
return -EFAULT;
}

if (!ret && count > 0) {
Expand Down Expand Up @@ -805,7 +851,7 @@ struct compat_fps {
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
compat_ulong_t caddr, compat_ulong_t cdata)
{
const struct user_regset_view *view = task_user_regset_view(child);
const struct user_regset_view *view = task_user_regset_view(current);
compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4];
struct pt_regs32 __user *pregs;
struct compat_fps __user *fps;
Expand Down Expand Up @@ -913,7 +959,7 @@ struct fps {

long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
const struct user_regset_view *view = task_user_regset_view(child);
const struct user_regset_view *view = task_user_regset_view(current);
unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4];
struct pt_regs __user *pregs;
struct fps __user *fps;
Expand Down

0 comments on commit d786a4a

Please sign in to comment.