]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.0.23/i387-fix-x86-64-preemption-unsafe-user-stack-save-restore.patch
Fix up backported ptrace patch
[thirdparty/kernel/stable-queue.git] / releases / 3.0.23 / i387-fix-x86-64-preemption-unsafe-user-stack-save-restore.patch
1 From 15d8791cae75dca27bfda8ecfe87dca9379d6bb0 Mon Sep 17 00:00:00 2001
2 From: Linus Torvalds <torvalds@linux-foundation.org>
3 Date: Thu, 16 Feb 2012 09:15:04 -0800
4 Subject: i387: fix x86-64 preemption-unsafe user stack save/restore
5
6 From: Linus Torvalds <torvalds@linux-foundation.org>
7
8 commit 15d8791cae75dca27bfda8ecfe87dca9379d6bb0 upstream.
9
10 Commit 5b1cbac37798 ("i387: make irq_fpu_usable() tests more robust")
11 added a sanity check to the #NM handler to verify that we never cause
12 the "Device Not Available" exception in kernel mode.
13
14 However, that check actually pinpointed a (fundamental) race where we do
15 cause that exception as part of the signal stack FPU state save/restore
16 code.
17
18 Because we use the floating point instructions themselves to save and
19 restore state directly from user mode, we cannot do that atomically with
20 testing the TS_USEDFPU bit: the user mode access itself may cause a page
21 fault, which causes a task switch, which saves and restores the FP/MMX
22 state from the kernel buffers.
23
24 This kind of "recursive" FP state save is fine per se, but it means that
25 when the signal stack save/restore gets restarted, it will now take the
26 '#NM' exception we originally tried to avoid. With preemption this can
27 happen even without the page fault - but because of the user access, we
28 cannot just disable preemption around the save/restore instruction.
29
30 There are various ways to solve this, including using the
31 "enable/disable_page_fault()" helpers to not allow page faults at all
32 during the sequence, and fall back to copying things by hand without the
33 use of the native FP state save/restore instructions.
34
35 However, the simplest thing to do is to just allow the #NM from kernel
36 space, but fix the race in setting and clearing CR0.TS that this all
37 exposed: the TS bit changes and the TS_USEDFPU bit absolutely have to be
38 atomic wrt scheduling, so while the actual state save/restore can be
39 interrupted and restarted, the act of actually clearing/setting CR0.TS
40 and the TS_USEDFPU bit together must not.
41
42 Instead of just adding random "preempt_disable/enable()" calls to what
43 is already excessively ugly code, this introduces some helper functions
44 that mostly mirror the "kernel_fpu_begin/end()" functionality, just for
45 the user state instead.
46
47 Those helper functions should probably eventually replace the other
48 ad-hoc CR0.TS and TS_USEDFPU tests too, but I'll need to think about it
49 some more: the task switching functionality in particular needs to
50 expose the difference between the 'prev' and 'next' threads, while the
51 new helper functions intentionally were written to only work with
52 'current'.
53
54 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
55 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
56
57 ---
58 arch/x86/include/asm/i387.h | 42 ++++++++++++++++++++++++++++++++++++++++++
59 arch/x86/kernel/traps.c | 1 -
60 arch/x86/kernel/xsave.c | 10 +++-------
61 3 files changed, 45 insertions(+), 8 deletions(-)
62
63 --- a/arch/x86/include/asm/i387.h
64 +++ b/arch/x86/include/asm/i387.h
65 @@ -400,6 +400,48 @@ static inline void irq_ts_restore(int TS
66 }
67
68 /*
69 + * The question "does this thread have fpu access?"
70 + * is slightly racy, since preemption could come in
71 + * and revoke it immediately after the test.
72 + *
73 + * However, even in that very unlikely scenario,
74 + * we can just assume we have FPU access - typically
75 + * to save the FP state - we'll just take a #NM
76 + * fault and get the FPU access back.
77 + *
78 + * The actual user_fpu_begin/end() functions
79 + * need to be preemption-safe, though.
80 + *
81 + * NOTE! user_fpu_end() must be used only after you
82 + * have saved the FP state, and user_fpu_begin() must
83 + * be used only immediately before restoring it.
84 + * These functions do not do any save/restore on
85 + * their own.
86 + */
87 +static inline int user_has_fpu(void)
88 +{
89 + return current_thread_info()->status & TS_USEDFPU;
90 +}
91 +
92 +static inline void user_fpu_end(void)
93 +{
94 + preempt_disable();
95 + current_thread_info()->status &= ~TS_USEDFPU;
96 + stts();
97 + preempt_enable();
98 +}
99 +
100 +static inline void user_fpu_begin(void)
101 +{
102 + preempt_disable();
103 + if (!user_has_fpu()) {
104 + clts();
105 + current_thread_info()->status |= TS_USEDFPU;
106 + }
107 + preempt_enable();
108 +}
109 +
110 +/*
111 * These disable preemption on their own and are safe
112 */
113 static inline void save_init_fpu(struct task_struct *tsk)
114 --- a/arch/x86/kernel/traps.c
115 +++ b/arch/x86/kernel/traps.c
116 @@ -777,7 +777,6 @@ EXPORT_SYMBOL_GPL(math_state_restore);
117 dotraplinkage void __kprobes
118 do_device_not_available(struct pt_regs *regs, long error_code)
119 {
120 - WARN_ON_ONCE(!user_mode_vm(regs));
121 #ifdef CONFIG_MATH_EMULATION
122 if (read_cr0() & X86_CR0_EM) {
123 struct math_emu_info info = { };
124 --- a/arch/x86/kernel/xsave.c
125 +++ b/arch/x86/kernel/xsave.c
126 @@ -168,7 +168,7 @@ int save_i387_xstate(void __user *buf)
127 if (!used_math())
128 return 0;
129
130 - if (task_thread_info(tsk)->status & TS_USEDFPU) {
131 + if (user_has_fpu()) {
132 if (use_xsave())
133 err = xsave_user(buf);
134 else
135 @@ -176,8 +176,7 @@ int save_i387_xstate(void __user *buf)
136
137 if (err)
138 return err;
139 - task_thread_info(tsk)->status &= ~TS_USEDFPU;
140 - stts();
141 + user_fpu_end();
142 } else {
143 sanitize_i387_state(tsk);
144 if (__copy_to_user(buf, &tsk->thread.fpu.state->fxsave,
145 @@ -292,10 +291,7 @@ int restore_i387_xstate(void __user *buf
146 return err;
147 }
148
149 - if (!(task_thread_info(current)->status & TS_USEDFPU)) {
150 - clts();
151 - task_thread_info(current)->status |= TS_USEDFPU;
152 - }
153 + user_fpu_begin();
154 if (use_xsave())
155 err = restore_user_xstate(buf);
156 else