Copy FCSR in the FP regset to match the original pre-regset core dumper. The code paths for where sizeof(union fpureg) == sizeof(elf_fpreg_t) already do so, but they actually copy 4 bytes more than they should do as FCSR is only 32 bits. The not equal code paths do not copy it at all. Therefore change the copy to be done explicitly (with the correct size) for both paths. Additionally, clear the cause bits from FCSR when setting the FP regset to avoid the possibility of causing an FP exception (and an oops) in the kernel. Signed-off-by: Alex Smith <alex@xxxxxxxxxxxxxxxx> Cc: Paul Burton <paul.burton@xxxxxxxxxx> Cc: <stable@xxxxxxxxxxxxxxx> # v3.13+ --- This patch incorporates a fix for another instance of the bug fixed by Paul Burton's patch "MIPS: prevent user from setting FCSR cause bits" - the code path in fpr_set for sizeof(fpureg) == sizeof(elf_fpreg_t) copied fcr31 without clearing cause bits. I've incorporated a fix for it into this patch to so that it's easier to apply both patches without conflicts. --- arch/mips/kernel/ptrace.c | 61 +++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 8bd13ed..ffc2e37 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -409,23 +409,28 @@ static int fpr_get(struct task_struct *target, int err; u64 fpr_val; - /* XXX fcr31 */ - - if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); - - for (i = 0; i < NUM_FPU_REGS; i++) { - fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); + if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) { err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, - &fpr_val, i * sizeof(elf_fpreg_t), - (i + 1) * sizeof(elf_fpreg_t)); + &target->thread.fpu.fpr, + 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); if (err) return err; + } else { + for (i = 0; i < NUM_FPU_REGS; i++) { + fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); + err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fpr_val, + i * sizeof(elf_fpreg_t), + (i + 1) * sizeof(elf_fpreg_t)); + if (err) + return err; + } } - return 0; + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fcr31, + NUM_FPU_REGS * sizeof(elf_fpreg_t), + (NUM_FPU_REGS * sizeof(elf_fpreg_t)) + sizeof(u32)); } static int fpr_set(struct task_struct *target, @@ -436,23 +441,33 @@ static int fpr_set(struct task_struct *target, unsigned i; int err; u64 fpr_val; + u32 fcr31; - /* XXX fcr31 */ - - if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) - return user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &target->thread.fpu, - 0, sizeof(elf_fpregset_t)); - - for (i = 0; i < NUM_FPU_REGS; i++) { + if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t)) { err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, - &fpr_val, i * sizeof(elf_fpreg_t), - (i + 1) * sizeof(elf_fpreg_t)); + &target->thread.fpu.fpr, + 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); if (err) return err; - set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); + } else { + for (i = 0; i < NUM_FPU_REGS; i++) { + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fpr_val, + i * sizeof(elf_fpreg_t), + (i + 1) * sizeof(elf_fpreg_t)); + if (err) + return err; + set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); + } } + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fcr31, + NUM_FPU_REGS * sizeof(elf_fpreg_t), + (NUM_FPU_REGS * sizeof(elf_fpreg_t)) + sizeof(u32)); + if (err) + return err; + + target->thread.fpu.fcr31 = fcr31 & ~FPU_CSR_ALL_X; return 0; } -- 1.9.1