From 36c8673f90c8e244b10d1905eb08f4f61c033a25 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 21 Feb 2020 00:11:55 -0500 Subject: x86: copy_fpstate_to_sigframe(): have fpregs_soft_get() use kernel buffer ... then copy_to_user() the results Signed-off-by: Al Viro --- arch/x86/kernel/fpu/signal.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index 9393a445d73c..e0b832df7404 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -170,14 +170,14 @@ int copy_fpstate_to_sigframe(void __user *buf, void __user *buf_fx, int size) ia32_fxstate &= (IS_ENABLED(CONFIG_X86_32) || IS_ENABLED(CONFIG_IA32_EMULATION)); + if (!static_cpu_has(X86_FEATURE_FPU)) { + struct user_i387_ia32_struct fp; + fpregs_soft_get(current, NULL, 0, sizeof(fp), &fp, NULL); + return copy_to_user(buf, &fp, sizeof(fp)) ? -EFAULT : 0; + } + if (!access_ok(buf, size)) return -EACCES; - - if (!static_cpu_has(X86_FEATURE_FPU)) - return fpregs_soft_get(current, NULL, 0, - sizeof(struct user_i387_ia32_struct), NULL, - (struct _fpstate_32 __user *) buf) ? -1 : 1; - retry: /* * Load the FPU registers if they are not valid for the current task. -- cgit From 4dfa103e82aefe15ac525c0d4853653a774be494 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 7 Jun 2020 11:48:25 -0400 Subject: x86: kill dump_fpu() dead since the removal of aout coredump support... Signed-off-by: Al Viro --- arch/x86/include/asm/fpu/internal.h | 1 - arch/x86/kernel/fpu/regset.c | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/arch/x86/include/asm/fpu/internal.h b/arch/x86/include/asm/fpu/internal.h index 42159f45bf9c..0c85a08176c0 100644 --- a/arch/x86/include/asm/fpu/internal.h +++ b/arch/x86/include/asm/fpu/internal.h @@ -34,7 +34,6 @@ extern int fpu__copy(struct task_struct *dst, struct task_struct *src); extern void fpu__clear_user_states(struct fpu *fpu); extern void fpu__clear_all(struct fpu *fpu); extern int fpu__exception_code(struct fpu *fpu, int trap_nr); -extern int dump_fpu(struct pt_regs *ptregs, struct user_i387_struct *fpstate); /* * Boot time FPU initialization functions: diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c index bd1d0649f8ce..91f80aca27fb 100644 --- a/arch/x86/kernel/fpu/regset.c +++ b/arch/x86/kernel/fpu/regset.c @@ -356,20 +356,4 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset, return ret; } -/* - * FPU state for core dumps. - * This is only used for a.out dumps now. - * It is declared generically using elf_fpregset_t (which is - * struct user_i387_struct) but is in fact only used for 32-bit - * dumps, so on 64-bit it is really struct user_i387_ia32_struct. - */ -int dump_fpu(struct pt_regs *regs, struct user_i387_struct *ufpu) -{ - struct task_struct *tsk = current; - - return !fpregs_get(tsk, NULL, 0, sizeof(struct user_i387_ia32_struct), - ufpu, NULL); -} -EXPORT_SYMBOL(dump_fpu); - #endif /* CONFIG_X86_32 || CONFIG_IA32_EMULATION */ -- cgit From 4c35bf3ae948d6232f3d2c6fbf2008c341490683 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 17:55:02 -0400 Subject: [ia64] sanitize elf_access_gpreg() The function takes the register number, finds the corresponding field of pt_regs for registers that are saved there or does the unwind for the registers that end up spilled on the kernel stack. Then it reads from or writes to the resulting location. Unfortunately, finding the required pt_regs field is done by rather horrible switch. It's microoptimized in all the wrong places - it even uses the knowledge that fields for r8..r11 follow each other in pt_regs layout, while r12..r13 are not adjacent to those, etc. All of that is to encode the mapping from register numbers to offsets + the information that r4..r7 are not to be found in pt_regs. It's deeply in nasal demon territory, at that - the games it plays with pointer arithmetics on addresses of structure members are undefined behaviour. Valid C ends up with better code in this case: just initialize a constant array with offsets of relevant pt_regs fields and we don't need that switch anymore. Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 57 +++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 82aaacf64583..b9d068903b98 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1273,52 +1273,41 @@ struct regset_getset { int ret; }; +static const ptrdiff_t pt_offsets[16] = +{ +#define R(n) offsetof(struct pt_regs, r##n) + [0] = -1, R(1), R(2), R(3), + [4] = -1, [5] = -1, [6] = -1, [7] = -1, + R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), +#undef R +}; + static int access_elf_gpreg(struct task_struct *target, struct unw_frame_info *info, unsigned long addr, unsigned long *data, int write_access) { - struct pt_regs *pt; - unsigned long *ptr = NULL; - int ret; - char nat = 0; + struct pt_regs *pt = task_pt_regs(target); + unsigned reg = addr / sizeof(unsigned long); + ptrdiff_t d = pt_offsets[reg]; - pt = task_pt_regs(target); - switch (addr) { - case ELF_GR_OFFSET(1): - ptr = &pt->r1; - break; - case ELF_GR_OFFSET(2): - case ELF_GR_OFFSET(3): - ptr = (void *)&pt->r2 + (addr - ELF_GR_OFFSET(2)); - break; - case ELF_GR_OFFSET(4) ... ELF_GR_OFFSET(7): + if (d >= 0) { + unsigned long *ptr = (void *)pt + d; + if (write_access) + *ptr = *data; + else + *data = *ptr; + return 0; + } else { + char nat = 0; if (write_access) { /* read NaT bit first: */ unsigned long dummy; - - ret = unw_get_gr(info, addr/8, &dummy, &nat); + int ret = unw_get_gr(info, reg, &dummy, &nat); if (ret < 0) return ret; } - return unw_access_gr(info, addr/8, data, &nat, write_access); - case ELF_GR_OFFSET(8) ... ELF_GR_OFFSET(11): - ptr = (void *)&pt->r8 + addr - ELF_GR_OFFSET(8); - break; - case ELF_GR_OFFSET(12): - case ELF_GR_OFFSET(13): - ptr = (void *)&pt->r12 + addr - ELF_GR_OFFSET(12); - break; - case ELF_GR_OFFSET(14): - ptr = &pt->r14; - break; - case ELF_GR_OFFSET(15): - ptr = &pt->r15; + return unw_access_gr(info, reg, data, &nat, write_access); } - if (write_access) - *ptr = *data; - else - *data = *ptr; - return 0; } static int -- cgit From e2115cf3cc57fc4b3f6b0ea4f746e5ab6dc9c2c0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 17:55:02 -0400 Subject: [ia64] teach elf_access_reg() to handle the missing range (r16..r31) Now it's easy to make elf_access_gpreg() handle the rest of global registers (r16..r31). That gets rid of the hole in the registers elf_access_reg() can handle, which will allow to simplify its callers later in the series. Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index b9d068903b98..37b5140d0dfe 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1273,12 +1273,14 @@ struct regset_getset { int ret; }; -static const ptrdiff_t pt_offsets[16] = +static const ptrdiff_t pt_offsets[32] = { #define R(n) offsetof(struct pt_regs, r##n) [0] = -1, R(1), R(2), R(3), [4] = -1, [5] = -1, [6] = -1, [7] = -1, R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), + R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), + R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31), #undef R }; @@ -1479,7 +1481,7 @@ static int access_elf_reg(struct task_struct *target, struct unw_frame_info *info, unsigned long addr, unsigned long *data, int write_access) { - if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(15)) + if (addr >= ELF_GR_OFFSET(1) && addr <= ELF_GR_OFFSET(31)) return access_elf_gpreg(target, info, addr, data, write_access); else if (addr >= ELF_BR_OFFSET(0) && addr <= ELF_BR_OFFSET(7)) return access_elf_breg(target, info, addr, data, write_access); -- cgit From a79ca8e7b9d56c762c3cd53465fde62f8ca41acf Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 18:00:56 -0400 Subject: [ia64] regularize do_gpregs_[gs]et() now access_elf_reg() does the right thing for everything other than r0, we can simplify do_grepgs_[gs]et() Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 155 ++++++++++------------------------------------ 1 file changed, 31 insertions(+), 124 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 37b5140d0dfe..be635f6f93c9 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1491,10 +1491,7 @@ access_elf_reg(struct task_struct *target, struct unw_frame_info *info, void do_gpregs_get(struct unw_frame_info *info, void *arg) { - struct pt_regs *pt; struct regset_getset *dst = arg; - elf_greg_t tmp[16]; - unsigned int i, index, min_copy; if (unw_unwind_to_user(info) < 0) return; @@ -1517,160 +1514,70 @@ void do_gpregs_get(struct unw_frame_info *info, void *arg) &dst->u.get.kbuf, &dst->u.get.ubuf, 0, ELF_GR_OFFSET(1)); - if (dst->ret || dst->count == 0) - return; - } - - /* gr1 - gr15 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { - index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); - min_copy = ELF_GR_OFFSET(16) > (dst->pos + dst->count) ? - (dst->pos + dst->count) : ELF_GR_OFFSET(16); - for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), - index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 0) < 0) { - dst->ret = -EIO; - return; - } - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); - if (dst->ret || dst->count == 0) - return; - } - - /* r16-r31 */ - if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { - pt = task_pt_regs(dst->target); - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, &pt->r16, - ELF_GR_OFFSET(16), ELF_NAT_OFFSET); - if (dst->ret || dst->count == 0) + if (dst->ret) return; } - /* nat, pr, b0 - b7 */ - if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { - index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); - min_copy = ELF_CR_IIP_OFFSET > (dst->pos + dst->count) ? - (dst->pos + dst->count) : ELF_CR_IIP_OFFSET; - for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), - index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 0) < 0) { + while (dst->count && dst->pos < ELF_AR_END_OFFSET) { + unsigned int n, from, to; + elf_greg_t tmp[16]; + + from = dst->pos; + to = from + min(dst->count, (unsigned)sizeof(tmp)); + if (to > ELF_AR_END_OFFSET) + to = ELF_AR_END_OFFSET; + for (n = 0; from < to; from += sizeof(elf_greg_t), n++) { + if (access_elf_reg(dst->target, info, from, + &tmp[n], 0) < 0) { dst->ret = -EIO; return; } + } dst->ret = user_regset_copyout(&dst->pos, &dst->count, &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); - if (dst->ret || dst->count == 0) + dst->pos, to); + if (dst->ret) return; } - - /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat - * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd - */ - if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { - index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); - min_copy = ELF_AR_END_OFFSET > (dst->pos + dst->count) ? - (dst->pos + dst->count) : ELF_AR_END_OFFSET; - for (i = dst->pos; i < min_copy; i += sizeof(elf_greg_t), - index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 0) < 0) { - dst->ret = -EIO; - return; - } - dst->ret = user_regset_copyout(&dst->pos, &dst->count, - &dst->u.get.kbuf, &dst->u.get.ubuf, tmp, - ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); - } } void do_gpregs_set(struct unw_frame_info *info, void *arg) { - struct pt_regs *pt; struct regset_getset *dst = arg; - elf_greg_t tmp[16]; - unsigned int i, index; if (unw_unwind_to_user(info) < 0) return; + if (!dst->count) + return; /* Skip r0 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(1)) { + if (dst->pos < ELF_GR_OFFSET(1)) { dst->ret = user_regset_copyin_ignore(&dst->pos, &dst->count, &dst->u.set.kbuf, &dst->u.set.ubuf, 0, ELF_GR_OFFSET(1)); - if (dst->ret || dst->count == 0) - return; - } - - /* gr1-gr15 */ - if (dst->count > 0 && dst->pos < ELF_GR_OFFSET(16)) { - i = dst->pos; - index = (dst->pos - ELF_GR_OFFSET(1)) / sizeof(elf_greg_t); - dst->ret = user_regset_copyin(&dst->pos, &dst->count, - &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, - ELF_GR_OFFSET(1), ELF_GR_OFFSET(16)); if (dst->ret) return; - for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 1) < 0) { - dst->ret = -EIO; - return; - } - if (dst->count == 0) - return; - } - - /* gr16-gr31 */ - if (dst->count > 0 && dst->pos < ELF_NAT_OFFSET) { - pt = task_pt_regs(dst->target); - dst->ret = user_regset_copyin(&dst->pos, &dst->count, - &dst->u.set.kbuf, &dst->u.set.ubuf, &pt->r16, - ELF_GR_OFFSET(16), ELF_NAT_OFFSET); - if (dst->ret || dst->count == 0) - return; } - /* nat, pr, b0 - b7 */ - if (dst->count > 0 && dst->pos < ELF_CR_IIP_OFFSET) { - i = dst->pos; - index = (dst->pos - ELF_NAT_OFFSET) / sizeof(elf_greg_t); - dst->ret = user_regset_copyin(&dst->pos, &dst->count, - &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, - ELF_NAT_OFFSET, ELF_CR_IIP_OFFSET); - if (dst->ret) - return; - for (; i < dst->pos; i += sizeof(elf_greg_t), index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 1) < 0) { - dst->ret = -EIO; - return; - } - if (dst->count == 0) - return; - } + while (dst->count && dst->pos < ELF_AR_END_OFFSET) { + unsigned int n, from, to; + elf_greg_t tmp[16]; - /* ip cfm psr ar.rsc ar.bsp ar.bspstore ar.rnat - * ar.ccv ar.unat ar.fpsr ar.pfs ar.lc ar.ec ar.csd ar.ssd - */ - if (dst->count > 0 && dst->pos < (ELF_AR_END_OFFSET)) { - i = dst->pos; - index = (dst->pos - ELF_CR_IIP_OFFSET) / sizeof(elf_greg_t); + from = dst->pos; + to = from + sizeof(tmp); + if (to > ELF_AR_END_OFFSET) + to = ELF_AR_END_OFFSET; + /* get up to 16 values */ dst->ret = user_regset_copyin(&dst->pos, &dst->count, &dst->u.set.kbuf, &dst->u.set.ubuf, tmp, - ELF_CR_IIP_OFFSET, ELF_AR_END_OFFSET); + from, to); if (dst->ret) return; - for ( ; i < dst->pos; i += sizeof(elf_greg_t), index++) - if (access_elf_reg(dst->target, info, i, - &tmp[index], 1) < 0) { + /* now copy them into registers */ + for (n = 0; from < dst->pos; from += sizeof(elf_greg_t), n++) + if (access_elf_reg(dst->target, info, from, + &tmp[n], 1) < 0) { dst->ret = -EIO; return; } -- cgit From 6bc4f16c6c9bed6ce5c3ab77b95397c8c88bdb66 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 18:46:45 -0400 Subject: [ia64] access_uarea(): stop bothering with gpregs_[gs]et() We know this won't be called for child == current, so we don't need to bother with callbacks, etc. - just do unw_init_from_blocked_task(), unw_unwind_to_user() and do the payload of gpregs_[gs]et(). For one register. Which is to say, access_elf_reg(). Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index be635f6f93c9..e0813c8e4b47 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1936,15 +1936,14 @@ access_uarea(struct task_struct *child, unsigned long addr, } if (pos != -1) { - if (write_access) - ret = gpregs_set(child, NULL, pos, - sizeof(unsigned long), data, NULL); - else - ret = gpregs_get(child, NULL, pos, - sizeof(unsigned long), data, NULL); - if (ret != 0) - return -1; - return 0; + struct unw_frame_info info; + + memset(&info, 0, sizeof(info)); + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return 0; + + return access_elf_reg(child, &info, pos, data, write_access); } /* access debug registers */ -- cgit From e3fdfa37a3fabea38b01af05893050136a6cd136 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 20:22:32 -0400 Subject: [ia64] access_uarea(): don't bother with fpregs_[gs]et() similar to previous commit... Signed-off-by: Al Viro --- arch/ia64/kernel/ptrace.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index e0813c8e4b47..eae7b094f608 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -1811,7 +1811,6 @@ access_uarea(struct task_struct *child, unsigned long addr, unsigned long *data, int write_access) { unsigned int pos = -1; /* an invalid value */ - int ret; unsigned long *ptr, regnum; if ((addr & 0x7) != 0) { @@ -1843,14 +1842,39 @@ access_uarea(struct task_struct *child, unsigned long addr, } if (pos != -1) { - if (write_access) - ret = fpregs_set(child, NULL, pos, - sizeof(unsigned long), data, NULL); - else - ret = fpregs_get(child, NULL, pos, - sizeof(unsigned long), data, NULL); - if (ret != 0) - return -1; + unsigned reg = pos / sizeof(elf_fpreg_t); + int which_half = (pos / sizeof(unsigned long)) & 1; + + if (reg < 32) { /* fr2-fr31 */ + struct unw_frame_info info; + elf_fpreg_t fpreg; + + memset(&info, 0, sizeof(info)); + unw_init_from_blocked_task(&info, child); + if (unw_unwind_to_user(&info) < 0) + return 0; + + if (unw_get_fr(&info, reg, &fpreg)) + return -1; + if (write_access) { + fpreg.u.bits[which_half] = *data; + if (unw_set_fr(&info, reg, fpreg)) + return -1; + } else { + *data = fpreg.u.bits[which_half]; + } + } else { /* fph */ + elf_fpreg_t *p = &child->thread.fph[reg - 32]; + unsigned long *bits = &p->u.bits[which_half]; + + ia64_sync_fph(child); + if (write_access) + *bits = *data; + else if (child->thread.flags & IA64_THREAD_FPH_VALID) + *data = *bits; + else + *data = 0; + } return 0; } -- cgit From 030754c99538ef83ba6aaf1cdfe33ec4fbbc0c0f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 12:51:42 -0400 Subject: sparc64: switch genregs32_get() to use of get_from_target() ... for fetching the register window from target's stack, rather than open-coding it. Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_64.c | 59 ++++++++++++------------------------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 7122efb4b1cc..f7b2ddfc81d6 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -518,10 +518,10 @@ static int genregs32_get(struct task_struct *target, void *kbuf, void __user *ubuf) { const struct pt_regs *regs = task_pt_regs(target); - compat_ulong_t __user *reg_window; compat_ulong_t *k = kbuf; compat_ulong_t __user *u = ubuf; - compat_ulong_t reg; + u32 uregs[16]; + u32 reg; if (target == current) flushw_user(); @@ -533,52 +533,25 @@ static int genregs32_get(struct task_struct *target, for (; count > 0 && pos < 16; count--) *k++ = regs->u_regs[pos++]; - reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; - reg_window -= 16; - if (target == current) { - for (; count > 0 && pos < 32; count--) { - if (get_user(*k++, ®_window[pos++])) - return -EFAULT; - } - } else { - for (; count > 0 && pos < 32; count--) { - if (access_process_vm(target, - (unsigned long) - ®_window[pos], - k, sizeof(*k), - FOLL_FORCE) - != sizeof(*k)) - return -EFAULT; - k++; - pos++; - } + if (count && pos < 32) { + if (get_from_target(target, regs->u_regs[UREG_I6], + uregs, sizeof(uregs))) + return -EFAULT; + for (; count > 0 && pos < 32; count--) + *k++ = uregs[pos++ - 16]; + } } else { - for (; count > 0 && pos < 16; count--) { + for (; count > 0 && pos < 16; count--) if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) return -EFAULT; - } - - reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; - reg_window -= 16; - if (target == current) { - for (; count > 0 && pos < 32; count--) { - if (get_user(reg, ®_window[pos++]) || - put_user(reg, u++)) - return -EFAULT; - } - } else { - for (; count > 0 && pos < 32; count--) { - if (access_process_vm(target, - (unsigned long) - ®_window[pos++], - ®, sizeof(reg), - FOLL_FORCE) - != sizeof(reg)) - return -EFAULT; - if (put_user(reg, u++)) + if (count && pos < 32) { + if (get_from_target(target, regs->u_regs[UREG_I6], + uregs, sizeof(uregs))) + return -EFAULT; + for (; count > 0 && pos < 32; count--) + if (put_user(uregs[pos++ - 16], u++)) return -EFAULT; - } } } while (count > 0) { -- cgit From 87d805331a69afdd7b3ee3d5c42760b747a4904f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 13:19:47 -0400 Subject: sparc32: get rid of odd callers of copy_regset_to_user() the life is much simpler if copy_regset_to_user() (and ->get()) gets called only with pos == 0; sparc32 PTRACE_GETREGS and PTRACE_GETFPREGS are among the few things that use it to fetch pieces of regset _not_ starting at the beginning. It's actually easier to define a separate regset that would provide what we need, rather than trying to cobble that from the one PTRACE_GETREGSET uses. Extra ->get() instances do not amount to much code and once we get the conversion of ->get() to new API (dependent upon the lack of weird callers of ->get()) they'll shrink a lot, along with the rest of ->get() instances... Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_32.c | 117 +++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/arch/sparc/kernel/ptrace_32.c b/arch/sparc/kernel/ptrace_32.c index 47eb315d411c..f72b7d2c4716 100644 --- a/arch/sparc/kernel/ptrace_32.c +++ b/arch/sparc/kernel/ptrace_32.c @@ -99,15 +99,13 @@ static int genregs32_get(struct task_struct *target, if (ret || !count) return ret; - if (pos < 32 * sizeof(u32)) { - if (regwindow32_get(target, regs, uregs)) - return -EFAULT; - ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - uregs, - 16 * sizeof(u32), 32 * sizeof(u32)); - if (ret || !count) - return ret; - } + if (regwindow32_get(target, regs, uregs)) + return -EFAULT; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + uregs, + 16 * sizeof(u32), 32 * sizeof(u32)); + if (ret) + return ret; uregs[0] = regs->psr; uregs[1] = regs->pc; @@ -288,6 +286,73 @@ static const struct user_regset sparc32_regsets[] = { }, }; +static int getregs_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; + u32 v[4]; + int ret; + + if (target == current) + flush_user_windows(); + + v[0] = regs->psr; + v[1] = regs->pc; + v[2] = regs->npc; + v[3] = regs->y; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + v, + 0 * sizeof(u32), 4 * sizeof(u32)); + if (ret) + return ret; + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 4 * sizeof(u32), 19 * sizeof(u32)); +} + +static int getfpregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = target->thread.float_regs; + int ret = 0; + +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (ret) + return ret; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 32 * sizeof(u32), 33 * sizeof(u32)); + if (ret) + return ret; + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 33 * sizeof(u32), 68 * sizeof(u32)); +} + +static const struct user_regset ptrace32_regsets[] = { + [REGSET_GENERAL] = { + .n = 19, .size = sizeof(u32), .get = getregs_get, + }, + [REGSET_FP] = { + .n = 68, .size = sizeof(u32), .get = getfpregs_get, + }, +}; + +static const struct user_regset_view ptrace32_view = { + .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) +}; + static const struct user_regset_view user_sparc32_view = { .name = "sparc", .e_machine = EM_SPARC, .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) @@ -327,15 +392,10 @@ long arch_ptrace(struct task_struct *child, long request, switch(request) { case PTRACE_GETREGS: { - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; } @@ -353,23 +413,10 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_GETFPREGS: { - ret = copy_regset_to_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_to_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); - - if (!ret) { - if (__put_user(0, &fps->fpqd) || - __put_user(0, &fps->flags) || - __put_user(0, &fps->extra) || - clear_user(fps->fpq, sizeof(fps->fpq))) - ret = -EFAULT; - } + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_FP, 0, + 68 * sizeof(u32), + fps); break; } -- cgit From 8f0329211b7001a47f94702380d188295eba8ef2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 22 May 2020 14:21:59 -0400 Subject: sparc64: get rid of odd callers of copy_regset_to_user() same as for sparc32, and that's it - no more caller of ->get() with non-zero pos. Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_64.c | 175 ++++++++++++++++++++++++++++++++---------- 1 file changed, 136 insertions(+), 39 deletions(-) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index f7b2ddfc81d6..1b1910b67ca4 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -258,7 +258,7 @@ 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 && count && pos < (32 * sizeof(u64))) { + if (!ret && count) { struct reg_window window; if (regwindow64_get(target, regs, &window)) @@ -506,6 +506,57 @@ static const struct user_regset sparc64_regsets[] = { }, }; +static int getregs64_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 = task_pt_regs(target); + int ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 0, 15 * sizeof(u64)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 15 * sizeof(u64), 16 * sizeof(u64)); + if (!ret) { + /* TSTATE, TPC, TNPC */ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + ®s->tstate, + 16 * sizeof(u64), + 19 * sizeof(u64)); + } + if (!ret) { + unsigned long y = regs->y; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &y, + 19 * sizeof(u64), + 20 * sizeof(u64)); + } + return ret; +} + +static const struct user_regset ptrace64_regsets[] = { + /* Format is: + * G1 --> G7 + * O0 --> O7 + * 0 + * TSTATE, TPC, TNPC, Y + */ + [REGSET_GENERAL] = { + .n = 20, .size = sizeof(u64), .get = getregs64_get, + }, +}; + +static const struct user_regset_view ptrace64_view = { + .regsets = ptrace64_regsets, .n = ARRAY_SIZE(ptrace64_regsets) +}; + static const struct user_regset_view user_sparc64_view = { .name = "sparc64", .e_machine = EM_SPARCV9, .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) @@ -533,7 +584,7 @@ static int genregs32_get(struct task_struct *target, for (; count > 0 && pos < 16; count--) *k++ = regs->u_regs[pos++]; - if (count && pos < 32) { + if (count) { if (get_from_target(target, regs->u_regs[UREG_I6], uregs, sizeof(uregs))) return -EFAULT; @@ -545,7 +596,7 @@ static int genregs32_get(struct task_struct *target, for (; count > 0 && pos < 16; count--) if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) return -EFAULT; - if (count && pos < 32) { + if (count) { if (get_from_target(target, regs->u_regs[UREG_I6], uregs, sizeof(uregs))) return -EFAULT; @@ -840,6 +891,76 @@ static const struct user_regset sparc32_regsets[] = { }, }; +static int getregs_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 = task_pt_regs(target); + u32 uregs[19]; + int i; + + if (target == current) + flushw_user(); + + uregs[0] = tstate_to_psr(regs->tstate); + uregs[1] = regs->tpc; + uregs[2] = regs->tnpc; + uregs[3] = regs->y; + for (i = 1; i < 16; i++) + uregs[3 + i] = regs->u_regs[i]; + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + uregs, + 0, 19 * sizeof(u32)); +} + +static int getfpregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs; + compat_ulong_t fsr; + int ret = 0; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + if (fprs & FPRS_FEF) { + fsr = task_thread_info(target)->xfsr[0]; + } else { + fsr = 0; + } + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fsr, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 33 * sizeof(u32), 68 * sizeof(u32)); + return ret; +} + +static const struct user_regset ptrace32_regsets[] = { + [REGSET_GENERAL] = { + .n = 19, .size = sizeof(u32), .get = getregs_get, + }, + [REGSET_FP] = { + .n = 68, .size = sizeof(u32), .get = getfpregs_get, + }, +}; + +static const struct user_regset_view ptrace32_view = { + .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) +}; + static const struct user_regset_view user_sparc32_view = { .name = "sparc", .e_machine = EM_SPARC, .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) @@ -889,15 +1010,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_GETREGS: - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; case PTRACE_SETREGS: @@ -913,22 +1029,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_GETFPREGS: - ret = copy_regset_to_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_to_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); - if (!ret) { - if (__put_user(0, &fps->flags) || - __put_user(0, &fps->extra) || - __put_user(0, &fps->fpqd) || - clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) - ret = -EFAULT; - } + ret = copy_regset_to_user(child, &ptrace32_view, + REGSET_FP, 0, + 68 * sizeof(u32), + fps); break; case PTRACE_SETFPREGS: @@ -999,17 +1103,10 @@ long arch_ptrace(struct task_struct *child, long request, break; case PTRACE_GETREGS64: - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 1 * sizeof(u64), - 15 * sizeof(u64), - &pregs->u_regs[0]); - if (!ret) { - /* XXX doesn't handle 'y' register correctly XXX */ - ret = copy_regset_to_user(child, view, REGSET_GENERAL, - 32 * sizeof(u64), - 4 * sizeof(u64), - &pregs->tstate); - } + ret = copy_regset_to_user(child, &ptrace64_view, + REGSET_GENERAL, 0, + 19 * sizeof(u64), + pregs); break; case PTRACE_SETREGS64: -- cgit From a96dacf98884151f6a738a98aab50f35391455ca Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 15 May 2020 11:22:26 -0400 Subject: arm64: take fetching compat reg out of pt_regs into a new helper Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 68b7f34a08f5..5b837741ab76 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1237,6 +1237,22 @@ enum compat_regset { REGSET_COMPAT_VFP, }; +static inline compat_ulong_t compat_get_user_reg(struct task_struct *task, int idx) +{ + struct pt_regs *regs = task_pt_regs(task); + + switch (idx) { + case 15: + return regs->pc; + case 16: + return pstate_to_compat_psr(regs->pstate); + case 17: + return regs->orig_x0; + default: + return regs->regs[idx]; + } +} + static int compat_gpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -1255,23 +1271,7 @@ static int compat_gpr_get(struct task_struct *target, return -EIO; for (i = 0; i < num_regs; ++i) { - unsigned int idx = start + i; - compat_ulong_t reg; - - switch (idx) { - case 15: - reg = task_pt_regs(target)->pc; - break; - case 16: - reg = task_pt_regs(target)->pstate; - reg = pstate_to_compat_psr(reg); - break; - case 17: - reg = task_pt_regs(target)->orig_x0; - break; - default: - reg = task_pt_regs(target)->regs[idx]; - } + compat_ulong_t reg = compat_get_user_reg(target, start + i); if (kbuf) { memcpy(kbuf, ®, sizeof(reg)); -- cgit From b44f384074d09d406b9223d60905e4badadc4741 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 15 May 2020 11:26:08 -0400 Subject: arm64: get rid of copy_regset_to_user() in compat_ptrace_read_user() Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 5b837741ab76..d5f3da5197a1 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1541,9 +1541,7 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, else if (off == COMPAT_PT_TEXT_END_ADDR) tmp = tsk->mm->end_code; else if (off < sizeof(compat_elf_gregset_t)) - return copy_regset_to_user(tsk, &user_aarch32_view, - REGSET_COMPAT_GPR, off, - sizeof(compat_ulong_t), ret); + tmp = compat_get_user_reg(tsk, off >> 2); else if (off >= COMPAT_USER_SZ) return -EIO; else -- cgit From d547175b54fa8104b0e2e5fd4b372cf04fa031ab Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 21:19:58 -0400 Subject: arm64: sanitize compat_ptrace_write_user() don't bother with copy_regset_from_user() (not to mention set_fs()) Signed-off-by: Al Viro --- arch/arm64/kernel/ptrace.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index d5f3da5197a1..9f769e862f68 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1553,8 +1553,8 @@ static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off, compat_ulong_t val) { - int ret; - mm_segment_t old_fs = get_fs(); + struct pt_regs newregs = *task_pt_regs(tsk); + unsigned int idx = off / 4; if (off & 3 || off >= COMPAT_USER_SZ) return -EIO; @@ -1562,14 +1562,25 @@ static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off, if (off >= sizeof(compat_elf_gregset_t)) return 0; - set_fs(KERNEL_DS); - ret = copy_regset_from_user(tsk, &user_aarch32_view, - REGSET_COMPAT_GPR, off, - sizeof(compat_ulong_t), - &val); - set_fs(old_fs); + switch (idx) { + case 15: + newregs.pc = val; + break; + case 16: + newregs.pstate = compat_psr_to_pstate(val); + break; + case 17: + newregs.orig_x0 = val; + break; + default: + newregs.regs[idx] = val; + } + + if (!valid_user_regs(&newregs.user_regs, tsk)) + return -EINVAL; - return ret; + *task_pt_regs(tsk) = newregs; + return 0; } #ifdef CONFIG_HAVE_HW_BREAKPOINT -- cgit From 98a7fbf391dbef6b11bc0a9039de0e81994ff923 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jun 2020 23:56:53 -0400 Subject: sparc32: get rid of odd callers of copy_regset_from_user() [a couple of unused variables left behind by the previous version spotted by kernel test robot ] Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_32.c | 113 ++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 38 deletions(-) diff --git a/arch/sparc/kernel/ptrace_32.c b/arch/sparc/kernel/ptrace_32.c index f72b7d2c4716..144e5a6d16aa 100644 --- a/arch/sparc/kernel/ptrace_32.c +++ b/arch/sparc/kernel/ptrace_32.c @@ -137,19 +137,18 @@ static int genregs32_set(struct task_struct *target, if (ret || !count) return ret; - 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; - } + 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; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &psr, 32 * sizeof(u32), 33 * sizeof(u32)); @@ -241,13 +240,11 @@ static int fpregs32_set(struct task_struct *target, user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 32 * sizeof(u32), 33 * sizeof(u32)); - if (!ret && count > 0) { + if (!ret) ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->thread.fsr, 33 * sizeof(u32), 34 * sizeof(u32)); - } - if (!ret) ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 34 * sizeof(u32), -1); @@ -313,6 +310,33 @@ static int getregs_get(struct task_struct *target, 4 * sizeof(u32), 19 * sizeof(u32)); } +static int setregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = target->thread.kregs; + u32 v[4]; + int ret; + + if (target == current) + flush_user_windows(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + v, + 0, 4 * sizeof(u32)); + if (ret) + return ret; + regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | + (v[0] & (PSR_ICC | PSR_SYSCALL)); + regs->pc = v[1]; + regs->npc = v[2]; + regs->y = v[3]; + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 4 * sizeof(u32) , 19 * sizeof(u32)); +} + static int getfpregs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -340,12 +364,37 @@ static int getfpregs_get(struct task_struct *target, 33 * sizeof(u32), 68 * sizeof(u32)); } +static int setfpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = target->thread.float_regs; + int ret; + +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (ret) + return ret; + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 32 * sizeof(u32), + 33 * sizeof(u32)); +} + static const struct user_regset ptrace32_regsets[] = { [REGSET_GENERAL] = { - .n = 19, .size = sizeof(u32), .get = getregs_get, + .n = 19, .size = sizeof(u32), + .get = getregs_get, .set = setregs_set, }, [REGSET_FP] = { - .n = 68, .size = sizeof(u32), .get = getfpregs_get, + .n = 68, .size = sizeof(u32), + .get = getfpregs_get, .set = setfpregs_set, }, }; @@ -380,12 +429,10 @@ long arch_ptrace(struct task_struct *child, long request, { unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; void __user *addr2p; - const struct user_regset_view *view; struct pt_regs __user *pregs; struct fps __user *fps; int ret; - view = task_user_regset_view(current); addr2p = (void __user *) addr2; pregs = (struct pt_regs __user *) addr; fps = (struct fps __user *) addr; @@ -400,15 +447,10 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_SETREGS: { - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; } @@ -421,15 +463,10 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_SETFPREGS: { - ret = copy_regset_from_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_from_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_FP, 0, + 33 * sizeof(u32), + fps); break; } -- cgit From b7e46c527d226bb654df615d742c41b7920d387e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 7 Jun 2020 00:34:26 -0400 Subject: sparc64: get rid of odd callers of copy_regset_from_user() Signed-off-by: Al Viro --- arch/sparc/kernel/ptrace_64.c | 177 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 33 deletions(-) diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 1b1910b67ca4..3c9eee12102a 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -541,6 +541,60 @@ static int getregs64_get(struct task_struct *target, return ret; } +static int setregs64_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + unsigned long y = regs->y; + unsigned long tstate; + int ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs->u_regs + 1, + 0 * sizeof(u64), + 15 * sizeof(u64)); + if (ret) + return ret; + ret =user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 15 * sizeof(u64), 16 * sizeof(u64)); + if (ret) + return ret; + /* TSTATE */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &tstate, + 16 * sizeof(u64), + 17 * sizeof(u64)); + if (ret) + return ret; + /* Only the condition codes and the "in syscall" + * state can be modified in the %tstate register. + */ + tstate &= (TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); + regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); + regs->tstate |= tstate; + + /* TPC, TNPC */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->tpc, + 17 * sizeof(u64), + 19 * sizeof(u64)); + if (ret) + return ret; + /* Y */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &y, + 19 * sizeof(u64), + 20 * sizeof(u64)); + if (!ret) + regs->y = y; + return ret; +} + static const struct user_regset ptrace64_regsets[] = { /* Format is: * G1 --> G7 @@ -549,7 +603,8 @@ static const struct user_regset ptrace64_regsets[] = { * TSTATE, TPC, TNPC, Y */ [REGSET_GENERAL] = { - .n = 20, .size = sizeof(u64), .get = getregs64_get, + .n = 20, .size = sizeof(u64), + .get = getregs64_get, .set = setregs64_set, }, }; @@ -914,6 +969,40 @@ static int getregs_get(struct task_struct *target, 0, 19 * sizeof(u32)); } +static int setregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + unsigned long tstate; + u32 uregs[19]; + int i, ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + uregs, + 0, 19 * sizeof(u32)); + if (ret) + return ret; + + tstate = regs->tstate; + tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); + tstate |= psr_to_tstate_icc(uregs[0]); + if (uregs[0] & PSR_SYSCALL) + tstate |= TSTATE_SYSCALL; + regs->tstate = tstate; + regs->tpc = uregs[1]; + regs->tnpc = uregs[2]; + regs->y = uregs[3]; + + for (i = 1; i < 15; i++) + regs->u_regs[i] = uregs[3 + i]; + return 0; +} + static int getfpregs_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, @@ -948,12 +1037,52 @@ static int getfpregs_get(struct task_struct *target, return ret; } +static int setfpregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs; + int ret; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) { + compat_ulong_t fsr; + unsigned long val; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fsr, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) { + val = task_thread_info(target)->xfsr[0]; + val &= 0xffffffff00000000UL; + val |= fsr; + task_thread_info(target)->xfsr[0] = val; + } + } + + fprs |= (FPRS_FEF | FPRS_DL); + task_thread_info(target)->fpsaved[0] = fprs; + return ret; +} + static const struct user_regset ptrace32_regsets[] = { [REGSET_GENERAL] = { - .n = 19, .size = sizeof(u32), .get = getregs_get, + .n = 19, .size = sizeof(u32), + .get = getregs_get, .set = setregs_set, }, [REGSET_FP] = { - .n = 68, .size = sizeof(u32), .get = getfpregs_get, + .n = 68, .size = sizeof(u32), + .get = getfpregs_get, .set = setfpregs_set, }, }; @@ -992,7 +1121,6 @@ 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(current); compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; struct pt_regs32 __user *pregs; struct compat_fps __user *fps; @@ -1017,15 +1145,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_SETREGS: - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u32), - 4 * sizeof(u32), - &pregs->psr); - if (!ret) - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u32), - 15 * sizeof(u32), - &pregs->u_regs[0]); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_GENERAL, 0, + 19 * sizeof(u32), + pregs); break; case PTRACE_GETFPREGS: @@ -1036,15 +1159,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, break; case PTRACE_SETFPREGS: - ret = copy_regset_from_user(child, view, REGSET_FP, - 0 * sizeof(u32), - 32 * sizeof(u32), - &fps->regs[0]); - if (!ret) - ret = copy_regset_from_user(child, view, REGSET_FP, - 33 * sizeof(u32), - 1 * sizeof(u32), - &fps->fsr); + ret = copy_regset_from_user(child, &ptrace32_view, + REGSET_FP, 0, + 33 * sizeof(u32), + fps); break; case PTRACE_READTEXT: @@ -1110,17 +1228,10 @@ long arch_ptrace(struct task_struct *child, long request, break; case PTRACE_SETREGS64: - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 1 * sizeof(u64), - 15 * sizeof(u64), - &pregs->u_regs[0]); - if (!ret) { - /* XXX doesn't handle 'y' register correctly XXX */ - ret = copy_regset_from_user(child, view, REGSET_GENERAL, - 32 * sizeof(u64), - 4 * sizeof(u64), - &pregs->tstate); - } + ret = copy_regset_from_user(child, &ptrace64_view, + REGSET_GENERAL, 0, + 19 * sizeof(u64), + pregs); break; case PTRACE_GETFPREGS64: -- cgit