Skip to content

Commit 247f499

Browse files
Dave MartinRussell King
authored andcommitted
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 <[email protected]> Signed-off-by: Dave Martin <[email protected]> Signed-off-by: Will Deacon <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent 2af276d commit 247f499

File tree

1 file changed

+4
-2
lines changed

1 file changed

+4
-2
lines changed

arch/arm/kernel/ptrace.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -699,10 +699,13 @@ static int vfp_set(struct task_struct *target,
699699
{
700700
int ret;
701701
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;
703703
const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs);
704704
const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr);
705705

706+
vfp_sync_hwstate(thread);
707+
new_vfp = thread->vfpstate.hard;
708+
706709
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
707710
&new_vfp.fpregs,
708711
user_fpregs_offset,
@@ -723,7 +726,6 @@ static int vfp_set(struct task_struct *target,
723726
if (ret)
724727
return ret;
725728

726-
vfp_sync_hwstate(thread);
727729
thread->vfpstate.hard = new_vfp;
728730
vfp_flush_hwstate(thread);
729731

0 commit comments

Comments
 (0)