MIPS: ptrace: Prevent writes to read-only FCSR bits

Correct the cases missed with commit 9b26616c8d ("MIPS: Respect the
ISA level in FCSR handling") and prevent writes to read-only FCSR bits
there.

This in particular applies to FP context initialisation where any IEEE
754-2008 bits preset by `mips_set_personality_nan' are cleared before
the relevant ptrace(2) call takes effect and the PTRACE_POKEUSR request
addressing FPC_CSR where no masking of read-only FCSR bits is done.

Remove the FCSR clearing from FP context initialisation then and unify
PTRACE_POKEUSR/FPC_CSR and PTRACE_SETFPREGS handling, by factoring out
code from `ptrace_setfpregs' and calling it from both places.

This mostly matters to soft float configurations where the emulator can
be switched this way to a mode which should not be accessible and cannot
be set with the CTC1 instruction.  With hard float configurations any
effect is transient anyway as read-only bits will retain their values at
the time the FP context is restored.

Signed-off-by: Maciej W. Rozycki <macro@imgtec.com>
Cc: stable@vger.kernel.org # v4.0+
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/13239/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Maciej W. Rozycki 2016-05-12 10:19:08 +01:00 committed by Ralf Baechle
parent 4249548454
commit abf378be49
1 changed files with 19 additions and 9 deletions

View File

@ -57,8 +57,7 @@ static void init_fp_ctx(struct task_struct *target)
/* Begin with data registers set to all 1s... */ /* Begin with data registers set to all 1s... */
memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr)); memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
/* ...and FCSR zeroed */ /* FCSR has been preset by `mips_set_personality_nan'. */
target->thread.fpu.fcr31 = 0;
/* /*
* Record that the target has "used" math, such that the context * Record that the target has "used" math, such that the context
@ -79,6 +78,22 @@ void ptrace_disable(struct task_struct *child)
clear_tsk_thread_flag(child, TIF_LOAD_WATCH); clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
} }
/*
* Poke at FCSR according to its mask. Don't set the cause bits as
* this is currently not handled correctly in FP context restoration
* and will cause an oops if a corresponding enable bit is set.
*/
static void ptrace_setfcr31(struct task_struct *child, u32 value)
{
u32 fcr31;
u32 mask;
value &= ~FPU_CSR_ALL_X;
fcr31 = child->thread.fpu.fcr31;
mask = boot_cpu_data.fpu_msk31;
child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);
}
/* /*
* Read a general register set. We always use the 64-bit format, even * Read a general register set. We always use the 64-bit format, even
* for 32-bit kernels and for 32-bit processes on a 64-bit kernel. * for 32-bit kernels and for 32-bit processes on a 64-bit kernel.
@ -159,9 +174,7 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
{ {
union fpureg *fregs; union fpureg *fregs;
u64 fpr_val; u64 fpr_val;
u32 fcr31;
u32 value; u32 value;
u32 mask;
int i; int i;
if (!access_ok(VERIFY_READ, data, 33 * 8)) if (!access_ok(VERIFY_READ, data, 33 * 8))
@ -176,10 +189,7 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
} }
__get_user(value, data + 64); __get_user(value, data + 64);
value &= ~FPU_CSR_ALL_X; ptrace_setfcr31(child, value);
fcr31 = child->thread.fpu.fcr31;
mask = boot_cpu_data.fpu_msk31;
child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);
/* FIR may not be written. */ /* FIR may not be written. */
@ -807,7 +817,7 @@ long arch_ptrace(struct task_struct *child, long request,
break; break;
#endif #endif
case FPC_CSR: case FPC_CSR:
child->thread.fpu.fcr31 = data & ~FPU_CSR_ALL_X; ptrace_setfcr31(child, data);
break; break;
case DSP_BASE ... DSP_BASE + 5: { case DSP_BASE ... DSP_BASE + 5: {
dspreg_t *dregs; dspreg_t *dregs;