[SPARC]: Fix several regset and ptrace bugs.
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>
This commit is contained in:
parent
ad4f957640
commit
d786a4a659
|
@ -325,7 +325,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
||||||
const struct user_regset_view *view;
|
const struct user_regset_view *view;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
view = task_user_regset_view(child);
|
view = task_user_regset_view(current);
|
||||||
|
|
||||||
switch(request) {
|
switch(request) {
|
||||||
case PTRACE_GETREGS: {
|
case PTRACE_GETREGS: {
|
||||||
|
|
|
@ -114,6 +114,85 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
|
||||||
preempt_enable();
|
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 {
|
enum sparc_regset {
|
||||||
REGSET_GENERAL,
|
REGSET_GENERAL,
|
||||||
REGSET_FP,
|
REGSET_FP,
|
||||||
|
@ -133,25 +212,13 @@ static int genregs64_get(struct task_struct *target,
|
||||||
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
||||||
regs->u_regs,
|
regs->u_regs,
|
||||||
0, 16 * sizeof(u64));
|
0, 16 * sizeof(u64));
|
||||||
if (!ret) {
|
if (!ret && count && pos < (32 * sizeof(u64))) {
|
||||||
unsigned long __user *reg_window = (unsigned long __user *)
|
struct reg_window window;
|
||||||
(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 (regwindow64_get(target, regs, &window))
|
||||||
|
return -EFAULT;
|
||||||
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
||||||
window,
|
&window,
|
||||||
16 * sizeof(u64),
|
16 * sizeof(u64),
|
||||||
32 * sizeof(u64));
|
32 * sizeof(u64));
|
||||||
}
|
}
|
||||||
|
@ -173,10 +240,11 @@ static int genregs64_get(struct task_struct *target,
|
||||||
36 * sizeof(u64));
|
36 * sizeof(u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
|
||||||
36 * sizeof(u64), -1);
|
36 * sizeof(u64), -1);
|
||||||
|
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,42 +262,20 @@ static int genregs64_set(struct task_struct *target,
|
||||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||||
regs->u_regs,
|
regs->u_regs,
|
||||||
0, 16 * sizeof(u64));
|
0, 16 * sizeof(u64));
|
||||||
if (!ret && count > 0) {
|
if (!ret && count && pos < (32 * sizeof(u64))) {
|
||||||
unsigned long __user *reg_window = (unsigned long __user *)
|
struct reg_window window;
|
||||||
(regs->u_regs[UREG_I6] + STACK_BIAS);
|
|
||||||
unsigned long window[16];
|
|
||||||
|
|
||||||
if (target == current) {
|
if (regwindow64_get(target, regs, &window))
|
||||||
if (copy_from_user(window, reg_window, sizeof(window)))
|
return -EFAULT;
|
||||||
return -EFAULT;
|
|
||||||
} else {
|
|
||||||
if (access_process_vm(target,
|
|
||||||
(unsigned long) reg_window,
|
|
||||||
window,
|
|
||||||
sizeof(window), 0) !=
|
|
||||||
sizeof(window))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
||||||
window,
|
&window,
|
||||||
16 * sizeof(u64),
|
16 * sizeof(u64),
|
||||||
32 * sizeof(u64));
|
32 * sizeof(u64));
|
||||||
if (!ret) {
|
|
||||||
if (target == current) {
|
if (!ret &&
|
||||||
if (copy_to_user(reg_window, window,
|
regwindow64_set(target, regs, &window))
|
||||||
sizeof(window)))
|
return -EFAULT;
|
||||||
return -EFAULT;
|
|
||||||
} else {
|
|
||||||
if (access_process_vm(target,
|
|
||||||
(unsigned long)
|
|
||||||
reg_window,
|
|
||||||
window,
|
|
||||||
sizeof(window), 1) !=
|
|
||||||
sizeof(window))
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret && count > 0) {
|
if (!ret && count > 0) {
|
||||||
|
@ -805,7 +851,7 @@ struct compat_fps {
|
||||||
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
||||||
compat_ulong_t caddr, compat_ulong_t cdata)
|
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];
|
compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4];
|
||||||
struct pt_regs32 __user *pregs;
|
struct pt_regs32 __user *pregs;
|
||||||
struct compat_fps __user *fps;
|
struct compat_fps __user *fps;
|
||||||
|
@ -913,7 +959,7 @@ struct fps {
|
||||||
|
|
||||||
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
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];
|
unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4];
|
||||||
struct pt_regs __user *pregs;
|
struct pt_regs __user *pregs;
|
||||||
struct fps __user *fps;
|
struct fps __user *fps;
|
||||||
|
|
Loading…
Reference in a new issue