diff options
author | Oleg Nesterov <oleg@redhat.com> | 2015-01-15 14:20:28 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-01-20 07:53:07 -0500 |
commit | 7575637ab293861a799f3bbafe0d8c597389f4e9 (patch) | |
tree | 52cc8c7a21eecaa53bbe3222864bcd3d8e482851 | |
parent | 33a3ebdc077fd85f1bf4d4586eea579b297461ae (diff) |
x86, fpu: Fix math_state_restore() race with kernel_fpu_begin()
math_state_restore() can race with kernel_fpu_begin() if irq comes
right after __thread_fpu_begin(), __save_init_fpu() will overwrite
fpu->state we are going to restore.
Add 2 simple helpers, kernel_fpu_disable() and kernel_fpu_enable()
which simply set/clear in_kernel_fpu, and change math_state_restore()
to exclude kernel_fpu_begin() in between.
Alternatively we could use local_irq_save/restore, but probably these
new helpers can have more users.
Perhaps they should disable/enable preemption themselves, in this case
we can remove preempt_disable() in __restore_xstate_sig().
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Rik van Riel <riel@redhat.com>
Cc: matt.fleming@intel.com
Cc: bp@suse.de
Cc: pbonzini@redhat.com
Cc: luto@amacapital.net
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Suresh Siddha <sbsiddha@gmail.com>
Link: http://lkml.kernel.org/r/20150115192028.GD27332@redhat.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | arch/x86/include/asm/i387.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/i387.c | 11 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 12 |
3 files changed, 20 insertions, 7 deletions
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h index 5e275d31802e..6eb6fcb83f63 100644 --- a/arch/x86/include/asm/i387.h +++ b/arch/x86/include/asm/i387.h | |||
@@ -51,6 +51,10 @@ static inline void kernel_fpu_end(void) | |||
51 | preempt_enable(); | 51 | preempt_enable(); |
52 | } | 52 | } |
53 | 53 | ||
54 | /* Must be called with preempt disabled */ | ||
55 | extern void kernel_fpu_disable(void); | ||
56 | extern void kernel_fpu_enable(void); | ||
57 | |||
54 | /* | 58 | /* |
55 | * Some instructions like VIA's padlock instructions generate a spurious | 59 | * Some instructions like VIA's padlock instructions generate a spurious |
56 | * DNA fault but don't modify SSE registers. And these instructions | 60 | * DNA fault but don't modify SSE registers. And these instructions |
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 12088a3f459f..81049ffab2d6 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c | |||
@@ -21,6 +21,17 @@ | |||
21 | 21 | ||
22 | static DEFINE_PER_CPU(bool, in_kernel_fpu); | 22 | static DEFINE_PER_CPU(bool, in_kernel_fpu); |
23 | 23 | ||
24 | void kernel_fpu_disable(void) | ||
25 | { | ||
26 | WARN_ON(this_cpu_read(in_kernel_fpu)); | ||
27 | this_cpu_write(in_kernel_fpu, true); | ||
28 | } | ||
29 | |||
30 | void kernel_fpu_enable(void) | ||
31 | { | ||
32 | this_cpu_write(in_kernel_fpu, false); | ||
33 | } | ||
34 | |||
24 | /* | 35 | /* |
25 | * Were we in an interrupt that interrupted kernel mode? | 36 | * Were we in an interrupt that interrupted kernel mode? |
26 | * | 37 | * |
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 88900e288021..fb4cb6adf225 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -788,18 +788,16 @@ void math_state_restore(void) | |||
788 | local_irq_disable(); | 788 | local_irq_disable(); |
789 | } | 789 | } |
790 | 790 | ||
791 | /* Avoid __kernel_fpu_begin() right after __thread_fpu_begin() */ | ||
792 | kernel_fpu_disable(); | ||
791 | __thread_fpu_begin(tsk); | 793 | __thread_fpu_begin(tsk); |
792 | |||
793 | /* | ||
794 | * Paranoid restore. send a SIGSEGV if we fail to restore the state. | ||
795 | */ | ||
796 | if (unlikely(restore_fpu_checking(tsk))) { | 794 | if (unlikely(restore_fpu_checking(tsk))) { |
797 | drop_init_fpu(tsk); | 795 | drop_init_fpu(tsk); |
798 | force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); | 796 | force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); |
799 | return; | 797 | } else { |
798 | tsk->thread.fpu_counter++; | ||
800 | } | 799 | } |
801 | 800 | kernel_fpu_enable(); | |
802 | tsk->thread.fpu_counter++; | ||
803 | } | 801 | } |
804 | EXPORT_SYMBOL_GPL(math_state_restore); | 802 | EXPORT_SYMBOL_GPL(math_state_restore); |
805 | 803 | ||