On 23 July 2014 15:12, Paul Burton <paul.burton@xxxxxxxxxx> wrote: > On Wed, Jul 23, 2014 at 02:40:10PM +0100, Alex Smith wrote: >> 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. > > Ah, I hadn't realised that ELF_NFPREG == 33, sneaky! That together with > the "XXX fcr31" comment led me to believe the FP regset didn't include > FCSR which is why I hadn't fixed the oops there or taken it into account > for the case where FPR size != sizeof(elf_fpreg_t) (ie. when MSA support > is enabled). > >> 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)); > > The only problem I can think of is that the final register in the regset > will still be treated as 64b (regset->size) as far as ptrace is > concerned, so I'm not sure how best to handle this. I presume the pre > regset core dump format placed the 32b FCSR value immediately after > the 64b $f31, as you have here? In which case we should probably at > least zero out the other 4 bytes of this final "register", assuming > the extra 4 bytes compared to the pre-regset version isn't a problem? Yes, this should now exactly match the old core dump code, which copies the 32-bit FCSR immediately after f31 (see dump_task_fpu). The last 4 bytes were still there with the old code, but it never actually touched them. You're right that this should zero it out. I'll do a v2 with that fixed. Thanks, Alex > > Thanks, > Paul > >> } >> >> 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 >>