aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/traps.c
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2013-02-23 19:19:14 -0500
committerFrederic Weisbecker <fweisbec@gmail.com>2013-03-07 11:10:11 -0500
commit6c1e0256fad84a843d915414e4b5973b7443d48d (patch)
treed93a2f27817f05c67840d947e0f9eb7f31fd24aa /arch/x86/kernel/traps.c
parent56dd9470d7c8734f055da2a6bac553caf4a468eb (diff)
context_tracking: Restore correct previous context state on exception exit
On exception exit, we restore the previous context tracking state based on the regs of the interrupted frame. Iff that frame is in user mode as stated by user_mode() helper, we restore the context tracking user mode. However there is a tiny chunck of low level arch code after we pass through user_enter() and until the CPU eventually resumes userspace. If an exception happens in this tiny area, exception_enter() correctly exits the context tracking user mode but exception_exit() won't restore it because of the value returned by user_mode(regs). As a result we may return to userspace with the wrong context tracking state. To fix this, change exception_enter() to return the context tracking state prior to its call and pass this saved state to exception_exit(). This restores the real context tracking state of the interrupted frame. (May be this patch was suggested to me, I don't recall exactly. If so, sorry for the missing credit). Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Li Zhong <zhong@linux.vnet.ibm.com> Cc: Kevin Hilman <khilman@linaro.org> Cc: Mats Liljegren <mats.liljegren@enea.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Namhyung Kim <namhyung.kim@lge.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'arch/x86/kernel/traps.c')
-rw-r--r--arch/x86/kernel/traps.c65
1 files changed, 41 insertions, 24 deletions
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index ecc4ccbdd0cf..ff6d2271cbe2 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -175,34 +175,38 @@ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
175#define DO_ERROR(trapnr, signr, str, name) \ 175#define DO_ERROR(trapnr, signr, str, name) \
176dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ 176dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
177{ \ 177{ \
178 exception_enter(regs); \ 178 enum ctx_state prev_state; \
179 \
180 prev_state = exception_enter(); \
179 if (notify_die(DIE_TRAP, str, regs, error_code, \ 181 if (notify_die(DIE_TRAP, str, regs, error_code, \
180 trapnr, signr) == NOTIFY_STOP) { \ 182 trapnr, signr) == NOTIFY_STOP) { \
181 exception_exit(regs); \ 183 exception_exit(prev_state); \
182 return; \ 184 return; \
183 } \ 185 } \
184 conditional_sti(regs); \ 186 conditional_sti(regs); \
185 do_trap(trapnr, signr, str, regs, error_code, NULL); \ 187 do_trap(trapnr, signr, str, regs, error_code, NULL); \
186 exception_exit(regs); \ 188 exception_exit(prev_state); \
187} 189}
188 190
189#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ 191#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
190dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ 192dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
191{ \ 193{ \
192 siginfo_t info; \ 194 siginfo_t info; \
195 enum ctx_state prev_state; \
196 \
193 info.si_signo = signr; \ 197 info.si_signo = signr; \
194 info.si_errno = 0; \ 198 info.si_errno = 0; \
195 info.si_code = sicode; \ 199 info.si_code = sicode; \
196 info.si_addr = (void __user *)siaddr; \ 200 info.si_addr = (void __user *)siaddr; \
197 exception_enter(regs); \ 201 prev_state = exception_enter(); \
198 if (notify_die(DIE_TRAP, str, regs, error_code, \ 202 if (notify_die(DIE_TRAP, str, regs, error_code, \
199 trapnr, signr) == NOTIFY_STOP) { \ 203 trapnr, signr) == NOTIFY_STOP) { \
200 exception_exit(regs); \ 204 exception_exit(prev_state); \
201 return; \ 205 return; \
202 } \ 206 } \
203 conditional_sti(regs); \ 207 conditional_sti(regs); \
204 do_trap(trapnr, signr, str, regs, error_code, &info); \ 208 do_trap(trapnr, signr, str, regs, error_code, &info); \
205 exception_exit(regs); \ 209 exception_exit(prev_state); \
206} 210}
207 211
208DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV, 212DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
@@ -225,14 +229,16 @@ DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check,
225/* Runs on IST stack */ 229/* Runs on IST stack */
226dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) 230dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
227{ 231{
228 exception_enter(regs); 232 enum ctx_state prev_state;
233
234 prev_state = exception_enter();
229 if (notify_die(DIE_TRAP, "stack segment", regs, error_code, 235 if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
230 X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) { 236 X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) {
231 preempt_conditional_sti(regs); 237 preempt_conditional_sti(regs);
232 do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); 238 do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
233 preempt_conditional_cli(regs); 239 preempt_conditional_cli(regs);
234 } 240 }
235 exception_exit(regs); 241 exception_exit(prev_state);
236} 242}
237 243
238dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) 244dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
@@ -240,7 +246,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
240 static const char str[] = "double fault"; 246 static const char str[] = "double fault";
241 struct task_struct *tsk = current; 247 struct task_struct *tsk = current;
242 248
243 exception_enter(regs); 249 exception_enter();
244 /* Return not checked because double check cannot be ignored */ 250 /* Return not checked because double check cannot be ignored */
245 notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); 251 notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
246 252
@@ -260,8 +266,9 @@ dotraplinkage void __kprobes
260do_general_protection(struct pt_regs *regs, long error_code) 266do_general_protection(struct pt_regs *regs, long error_code)
261{ 267{
262 struct task_struct *tsk; 268 struct task_struct *tsk;
269 enum ctx_state prev_state;
263 270
264 exception_enter(regs); 271 prev_state = exception_enter();
265 conditional_sti(regs); 272 conditional_sti(regs);
266 273
267#ifdef CONFIG_X86_32 274#ifdef CONFIG_X86_32
@@ -299,12 +306,14 @@ do_general_protection(struct pt_regs *regs, long error_code)
299 306
300 force_sig(SIGSEGV, tsk); 307 force_sig(SIGSEGV, tsk);
301exit: 308exit:
302 exception_exit(regs); 309 exception_exit(prev_state);
303} 310}
304 311
305/* May run on IST stack. */ 312/* May run on IST stack. */
306dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code) 313dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_code)
307{ 314{
315 enum ctx_state prev_state;
316
308#ifdef CONFIG_DYNAMIC_FTRACE 317#ifdef CONFIG_DYNAMIC_FTRACE
309 /* 318 /*
310 * ftrace must be first, everything else may cause a recursive crash. 319 * ftrace must be first, everything else may cause a recursive crash.
@@ -314,7 +323,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
314 ftrace_int3_handler(regs)) 323 ftrace_int3_handler(regs))
315 return; 324 return;
316#endif 325#endif
317 exception_enter(regs); 326 prev_state = exception_enter();
318#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP 327#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
319 if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, 328 if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
320 SIGTRAP) == NOTIFY_STOP) 329 SIGTRAP) == NOTIFY_STOP)
@@ -335,7 +344,7 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
335 preempt_conditional_cli(regs); 344 preempt_conditional_cli(regs);
336 debug_stack_usage_dec(); 345 debug_stack_usage_dec();
337exit: 346exit:
338 exception_exit(regs); 347 exception_exit(prev_state);
339} 348}
340 349
341#ifdef CONFIG_X86_64 350#ifdef CONFIG_X86_64
@@ -392,11 +401,12 @@ asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
392dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) 401dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
393{ 402{
394 struct task_struct *tsk = current; 403 struct task_struct *tsk = current;
404 enum ctx_state prev_state;
395 int user_icebp = 0; 405 int user_icebp = 0;
396 unsigned long dr6; 406 unsigned long dr6;
397 int si_code; 407 int si_code;
398 408
399 exception_enter(regs); 409 prev_state = exception_enter();
400 410
401 get_debugreg(dr6, 6); 411 get_debugreg(dr6, 6);
402 412
@@ -466,7 +476,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
466 debug_stack_usage_dec(); 476 debug_stack_usage_dec();
467 477
468exit: 478exit:
469 exception_exit(regs); 479 exception_exit(prev_state);
470} 480}
471 481
472/* 482/*
@@ -560,17 +570,21 @@ void math_error(struct pt_regs *regs, int error_code, int trapnr)
560 570
561dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) 571dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
562{ 572{
563 exception_enter(regs); 573 enum ctx_state prev_state;
574
575 prev_state = exception_enter();
564 math_error(regs, error_code, X86_TRAP_MF); 576 math_error(regs, error_code, X86_TRAP_MF);
565 exception_exit(regs); 577 exception_exit(prev_state);
566} 578}
567 579
568dotraplinkage void 580dotraplinkage void
569do_simd_coprocessor_error(struct pt_regs *regs, long error_code) 581do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
570{ 582{
571 exception_enter(regs); 583 enum ctx_state prev_state;
584
585 prev_state = exception_enter();
572 math_error(regs, error_code, X86_TRAP_XF); 586 math_error(regs, error_code, X86_TRAP_XF);
573 exception_exit(regs); 587 exception_exit(prev_state);
574} 588}
575 589
576dotraplinkage void 590dotraplinkage void
@@ -638,7 +652,9 @@ EXPORT_SYMBOL_GPL(math_state_restore);
638dotraplinkage void __kprobes 652dotraplinkage void __kprobes
639do_device_not_available(struct pt_regs *regs, long error_code) 653do_device_not_available(struct pt_regs *regs, long error_code)
640{ 654{
641 exception_enter(regs); 655 enum ctx_state prev_state;
656
657 prev_state = exception_enter();
642 BUG_ON(use_eager_fpu()); 658 BUG_ON(use_eager_fpu());
643 659
644#ifdef CONFIG_MATH_EMULATION 660#ifdef CONFIG_MATH_EMULATION
@@ -649,7 +665,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)
649 665
650 info.regs = regs; 666 info.regs = regs;
651 math_emulate(&info); 667 math_emulate(&info);
652 exception_exit(regs); 668 exception_exit(prev_state);
653 return; 669 return;
654 } 670 }
655#endif 671#endif
@@ -657,15 +673,16 @@ do_device_not_available(struct pt_regs *regs, long error_code)
657#ifdef CONFIG_X86_32 673#ifdef CONFIG_X86_32
658 conditional_sti(regs); 674 conditional_sti(regs);
659#endif 675#endif
660 exception_exit(regs); 676 exception_exit(prev_state);
661} 677}
662 678
663#ifdef CONFIG_X86_32 679#ifdef CONFIG_X86_32
664dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) 680dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
665{ 681{
666 siginfo_t info; 682 siginfo_t info;
683 enum ctx_state prev_state;
667 684
668 exception_enter(regs); 685 prev_state = exception_enter();
669 local_irq_enable(); 686 local_irq_enable();
670 687
671 info.si_signo = SIGILL; 688 info.si_signo = SIGILL;
@@ -677,7 +694,7 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
677 do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code, 694 do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
678 &info); 695 &info);
679 } 696 }
680 exception_exit(regs); 697 exception_exit(prev_state);
681} 698}
682#endif 699#endif
683 700