Commit 247f4993 authored by Dave Martin's avatar Dave Martin Committed by Russell King

ARM: 7307/1: vfp: fix ptrace regset modification race

In a preemptible kernel, vfp_set() can be preempted, causing the
hardware VFP context to be switched while the thread vfp state is
being read and modified.  This leads to a race condition which can
cause the thread vfp state to become corrupted if lazy VFP context
save occurs due to preemption in between the time thread->vfpstate
is read and the time the modified state is written back.

This may occur if preemption occurs during the execution of a
ptrace() call which modifies the VFP register state of a thread.
Such instances should be very rare in most realistic scenarios --
none has been reported, so far as I am aware.  Only uniprocessor
systems should be affected, since VFP context save is not currently
lazy in SMP kernels.

The problem was introduced by my earlier patch migrating to use
regsets to implement ptrace.

This patch does a vfp_sync_hwstate() before reading
thread->vfpstate, to make sure that the thread's VFP state is not
live in the hardware registers while the registers are modified.

Thanks to Will Deacon for spotting this.

Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarDave Martin <dave.martin@linaro.org>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 2af276df
...@@ -699,10 +699,13 @@ static int vfp_set(struct task_struct *target, ...@@ -699,10 +699,13 @@ static int vfp_set(struct task_struct *target,
{ {
int ret; int ret;
struct thread_info *thread = task_thread_info(target); struct thread_info *thread = task_thread_info(target);
struct vfp_hard_struct new_vfp = thread->vfpstate.hard; struct vfp_hard_struct new_vfp;
const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs);
const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr);
vfp_sync_hwstate(thread);
new_vfp = thread->vfpstate.hard;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&new_vfp.fpregs, &new_vfp.fpregs,
user_fpregs_offset, user_fpregs_offset,
...@@ -723,7 +726,6 @@ static int vfp_set(struct task_struct *target, ...@@ -723,7 +726,6 @@ static int vfp_set(struct task_struct *target,
if (ret) if (ret)
return ret; return ret;
vfp_sync_hwstate(thread);
thread->vfpstate.hard = new_vfp; thread->vfpstate.hard = new_vfp;
vfp_flush_hwstate(thread); vfp_flush_hwstate(thread);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment