aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Martin <dave.martin@linaro.org>2012-01-30 14:22:28 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-02-02 12:37:42 -0500
commit247f4993a5974e6759606c4d380748eecfd273ff (patch)
tree79cf9e348c8274154e64d69d4373bec416cf4dee
parent2af276dfb1722e97b190bd2e646b079a2aa674db (diff)
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: Dave Martin <dave.martin@linaro.org> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/kernel/ptrace.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index e1d5e1929fbd..d001be4e0ce1 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -699,10 +699,13 @@ static int vfp_set(struct task_struct *target,
699{ 699{
700 int ret; 700 int ret;
701 struct thread_info *thread = task_thread_info(target); 701 struct thread_info *thread = task_thread_info(target);
702 struct vfp_hard_struct new_vfp = thread->vfpstate.hard; 702 struct vfp_hard_struct new_vfp;
703 const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); 703 const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs);
704 const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); 704 const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr);
705 705
706 vfp_sync_hwstate(thread);
707 new_vfp = thread->vfpstate.hard;
708
706 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 709 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
707 &new_vfp.fpregs, 710 &new_vfp.fpregs,
708 user_fpregs_offset, 711 user_fpregs_offset,
@@ -723,7 +726,6 @@ static int vfp_set(struct task_struct *target,
723 if (ret) 726 if (ret)
724 return ret; 727 return ret;
725 728
726 vfp_sync_hwstate(thread);
727 thread->vfpstate.hard = new_vfp; 729 thread->vfpstate.hard = new_vfp;
728 vfp_flush_hwstate(thread); 730 vfp_flush_hwstate(thread);
729 731