diff options
author | Dave Hansen <dave.hansen@linux.intel.com> | 2015-06-07 14:37:01 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-06-09 06:24:30 -0400 |
commit | a84eeaa96b36a03188e1423349669c108d3a4bd7 (patch) | |
tree | 13f38f8a3859b26458b21cce4cf5ee22851313fb | |
parent | 04cd027bcba1ead7bfe39e7f1c6f4d993c4c3323 (diff) |
x86/mpx: Use the new get_xsave_field_ptr()API
The MPX registers (bndcsr/bndcfgu/bndstatus) are not directly
accessible via normal instructions. They essentially act as
if they were floating point registers and are saved/restored
along with those registers.
There are two main paths in the MPX code where we care about
the contents of these registers:
1. #BR (bounds) faults
2. the prctl() code where we are setting MPX up
Both of those paths _might_ be called without the FPU having
been used. That means that 'tsk->thread.fpu.state' might
never be allocated.
Also, fpu_save_init() is not preempt-safe. It was a bug to
call it without disabling preemption. The new
get_xsave_addr() calls unlazy_fpu() instead and properly
disables preemption.
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Dave Hansen <dave@sr71.net>
Cc: Fenghua Yu <fenghua.yu@intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rik van Riel <riel@redhat.com>
Cc: Suresh Siddha <sbsiddha@gmail.com>
Cc: bp@alien8.de
Link: http://lkml.kernel.org/r/20150607183701.BC0D37CF@viggo.jf.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/include/asm/mpx.h | 8 | ||||
-rw-r--r-- | arch/x86/kernel/traps.c | 17 | ||||
-rw-r--r-- | arch/x86/mm/mpx.c | 30 |
3 files changed, 27 insertions, 28 deletions
diff --git a/arch/x86/include/asm/mpx.h b/arch/x86/include/asm/mpx.h index f3c1b71d4fae..39f2d0ffe1e2 100644 --- a/arch/x86/include/asm/mpx.h +++ b/arch/x86/include/asm/mpx.h | |||
@@ -60,8 +60,8 @@ | |||
60 | 60 | ||
61 | #ifdef CONFIG_X86_INTEL_MPX | 61 | #ifdef CONFIG_X86_INTEL_MPX |
62 | siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, | 62 | siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, |
63 | struct xregs_state *xsave_buf); | 63 | struct task_struct *tsk); |
64 | int mpx_handle_bd_fault(struct xregs_state *xsave_buf); | 64 | int mpx_handle_bd_fault(struct task_struct *tsk); |
65 | static inline int kernel_managing_mpx_tables(struct mm_struct *mm) | 65 | static inline int kernel_managing_mpx_tables(struct mm_struct *mm) |
66 | { | 66 | { |
67 | return (mm->bd_addr != MPX_INVALID_BOUNDS_DIR); | 67 | return (mm->bd_addr != MPX_INVALID_BOUNDS_DIR); |
@@ -78,11 +78,11 @@ void mpx_notify_unmap(struct mm_struct *mm, struct vm_area_struct *vma, | |||
78 | unsigned long start, unsigned long end); | 78 | unsigned long start, unsigned long end); |
79 | #else | 79 | #else |
80 | static inline siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, | 80 | static inline siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, |
81 | struct xregs_state *xsave_buf) | 81 | struct task_struct *tsk) |
82 | { | 82 | { |
83 | return NULL; | 83 | return NULL; |
84 | } | 84 | } |
85 | static inline int mpx_handle_bd_fault(struct xregs_state *xsave_buf) | 85 | static inline int mpx_handle_bd_fault(struct task_struct *tsk) |
86 | { | 86 | { |
87 | return -EINVAL; | 87 | return -EINVAL; |
88 | } | 88 | } |
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index a2510f230195..42f15314b361 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -59,6 +59,7 @@ | |||
59 | #include <asm/fixmap.h> | 59 | #include <asm/fixmap.h> |
60 | #include <asm/mach_traps.h> | 60 | #include <asm/mach_traps.h> |
61 | #include <asm/alternative.h> | 61 | #include <asm/alternative.h> |
62 | #include <asm/fpu/xstate.h> | ||
62 | #include <asm/mpx.h> | 63 | #include <asm/mpx.h> |
63 | 64 | ||
64 | #ifdef CONFIG_X86_64 | 65 | #ifdef CONFIG_X86_64 |
@@ -371,9 +372,8 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) | |||
371 | dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) | 372 | dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) |
372 | { | 373 | { |
373 | struct task_struct *tsk = current; | 374 | struct task_struct *tsk = current; |
374 | struct xregs_state *xsave_buf; | ||
375 | enum ctx_state prev_state; | 375 | enum ctx_state prev_state; |
376 | struct bndcsr *bndcsr; | 376 | const struct bndcsr *bndcsr; |
377 | siginfo_t *info; | 377 | siginfo_t *info; |
378 | 378 | ||
379 | prev_state = exception_enter(); | 379 | prev_state = exception_enter(); |
@@ -392,12 +392,11 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) | |||
392 | 392 | ||
393 | /* | 393 | /* |
394 | * We need to look at BNDSTATUS to resolve this exception. | 394 | * We need to look at BNDSTATUS to resolve this exception. |
395 | * It is not directly accessible, though, so we need to | 395 | * A NULL here might mean that it is in its 'init state', |
396 | * do an xsave and then pull it out of the xsave buffer. | 396 | * which is all zeros which indicates MPX was not |
397 | * responsible for the exception. | ||
397 | */ | 398 | */ |
398 | copy_fpregs_to_fpstate(&tsk->thread.fpu); | 399 | bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR); |
399 | xsave_buf = &(tsk->thread.fpu.state.xsave); | ||
400 | bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR); | ||
401 | if (!bndcsr) | 400 | if (!bndcsr) |
402 | goto exit_trap; | 401 | goto exit_trap; |
403 | 402 | ||
@@ -408,11 +407,11 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) | |||
408 | */ | 407 | */ |
409 | switch (bndcsr->bndstatus & MPX_BNDSTA_ERROR_CODE) { | 408 | switch (bndcsr->bndstatus & MPX_BNDSTA_ERROR_CODE) { |
410 | case 2: /* Bound directory has invalid entry. */ | 409 | case 2: /* Bound directory has invalid entry. */ |
411 | if (mpx_handle_bd_fault(xsave_buf)) | 410 | if (mpx_handle_bd_fault(tsk)) |
412 | goto exit_trap; | 411 | goto exit_trap; |
413 | break; /* Success, it was handled */ | 412 | break; /* Success, it was handled */ |
414 | case 1: /* Bound violation. */ | 413 | case 1: /* Bound violation. */ |
415 | info = mpx_generate_siginfo(regs, xsave_buf); | 414 | info = mpx_generate_siginfo(regs, tsk); |
416 | if (IS_ERR(info)) { | 415 | if (IS_ERR(info)) { |
417 | /* | 416 | /* |
418 | * We failed to decode the MPX instruction. Act as if | 417 | * We failed to decode the MPX instruction. Act as if |
diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c index 2e0dfd39bd22..9d67e230b4fb 100644 --- a/arch/x86/mm/mpx.c +++ b/arch/x86/mm/mpx.c | |||
@@ -272,9 +272,9 @@ bad_opcode: | |||
272 | * The caller is expected to kfree() the returned siginfo_t. | 272 | * The caller is expected to kfree() the returned siginfo_t. |
273 | */ | 273 | */ |
274 | siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, | 274 | siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, |
275 | struct xregs_state *xsave_buf) | 275 | struct task_struct *tsk) |
276 | { | 276 | { |
277 | struct bndreg *bndregs, *bndreg; | 277 | const struct bndreg *bndregs, *bndreg; |
278 | siginfo_t *info = NULL; | 278 | siginfo_t *info = NULL; |
279 | struct insn insn; | 279 | struct insn insn; |
280 | uint8_t bndregno; | 280 | uint8_t bndregno; |
@@ -294,8 +294,8 @@ siginfo_t *mpx_generate_siginfo(struct pt_regs *regs, | |||
294 | err = -EINVAL; | 294 | err = -EINVAL; |
295 | goto err_out; | 295 | goto err_out; |
296 | } | 296 | } |
297 | /* get the bndregs _area_ of the xsave structure */ | 297 | /* get bndregs field from current task's xsave area */ |
298 | bndregs = get_xsave_addr(xsave_buf, XSTATE_BNDREGS); | 298 | bndregs = get_xsave_field_ptr(XSTATE_BNDREGS); |
299 | if (!bndregs) { | 299 | if (!bndregs) { |
300 | err = -EINVAL; | 300 | err = -EINVAL; |
301 | goto err_out; | 301 | goto err_out; |
@@ -342,7 +342,7 @@ err_out: | |||
342 | 342 | ||
343 | static __user void *task_get_bounds_dir(struct task_struct *tsk) | 343 | static __user void *task_get_bounds_dir(struct task_struct *tsk) |
344 | { | 344 | { |
345 | struct bndcsr *bndcsr; | 345 | const struct bndcsr *bndcsr; |
346 | 346 | ||
347 | if (!cpu_feature_enabled(X86_FEATURE_MPX)) | 347 | if (!cpu_feature_enabled(X86_FEATURE_MPX)) |
348 | return MPX_INVALID_BOUNDS_DIR; | 348 | return MPX_INVALID_BOUNDS_DIR; |
@@ -357,8 +357,7 @@ static __user void *task_get_bounds_dir(struct task_struct *tsk) | |||
357 | * The bounds directory pointer is stored in a register | 357 | * The bounds directory pointer is stored in a register |
358 | * only accessible if we first do an xsave. | 358 | * only accessible if we first do an xsave. |
359 | */ | 359 | */ |
360 | copy_fpregs_to_fpstate(&tsk->thread.fpu); | 360 | bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR); |
361 | bndcsr = get_xsave_addr(&tsk->thread.fpu.state.xsave, XSTATE_BNDCSR); | ||
362 | if (!bndcsr) | 361 | if (!bndcsr) |
363 | return MPX_INVALID_BOUNDS_DIR; | 362 | return MPX_INVALID_BOUNDS_DIR; |
364 | 363 | ||
@@ -389,9 +388,10 @@ int mpx_enable_management(struct task_struct *tsk) | |||
389 | * directory into XSAVE/XRSTOR Save Area and enable MPX through | 388 | * directory into XSAVE/XRSTOR Save Area and enable MPX through |
390 | * XRSTOR instruction. | 389 | * XRSTOR instruction. |
391 | * | 390 | * |
392 | * copy_xregs_to_kernel() is expected to be very expensive. Storing the bounds | 391 | * The copy_xregs_to_kernel() beneath get_xsave_field_ptr() is |
393 | * directory here means that we do not have to do xsave in the unmap | 392 | * expected to be relatively expensive. Storing the bounds |
394 | * path; we can just use mm->bd_addr instead. | 393 | * directory here means that we do not have to do xsave in the |
394 | * unmap path; we can just use mm->bd_addr instead. | ||
395 | */ | 395 | */ |
396 | bd_base = task_get_bounds_dir(tsk); | 396 | bd_base = task_get_bounds_dir(tsk); |
397 | down_write(&mm->mmap_sem); | 397 | down_write(&mm->mmap_sem); |
@@ -497,12 +497,12 @@ out_unmap: | |||
497 | * bound table is 16KB. With 64-bit mode, the size of BD is 2GB, | 497 | * bound table is 16KB. With 64-bit mode, the size of BD is 2GB, |
498 | * and the size of each bound table is 4MB. | 498 | * and the size of each bound table is 4MB. |
499 | */ | 499 | */ |
500 | static int do_mpx_bt_fault(struct xregs_state *xsave_buf) | 500 | static int do_mpx_bt_fault(struct task_struct *tsk) |
501 | { | 501 | { |
502 | unsigned long bd_entry, bd_base; | 502 | unsigned long bd_entry, bd_base; |
503 | struct bndcsr *bndcsr; | 503 | const struct bndcsr *bndcsr; |
504 | 504 | ||
505 | bndcsr = get_xsave_addr(xsave_buf, XSTATE_BNDCSR); | 505 | bndcsr = get_xsave_field_ptr(XSTATE_BNDCSR); |
506 | if (!bndcsr) | 506 | if (!bndcsr) |
507 | return -EINVAL; | 507 | return -EINVAL; |
508 | /* | 508 | /* |
@@ -525,7 +525,7 @@ static int do_mpx_bt_fault(struct xregs_state *xsave_buf) | |||
525 | return allocate_bt((long __user *)bd_entry); | 525 | return allocate_bt((long __user *)bd_entry); |
526 | } | 526 | } |
527 | 527 | ||
528 | int mpx_handle_bd_fault(struct xregs_state *xsave_buf) | 528 | int mpx_handle_bd_fault(struct task_struct *tsk) |
529 | { | 529 | { |
530 | /* | 530 | /* |
531 | * Userspace never asked us to manage the bounds tables, | 531 | * Userspace never asked us to manage the bounds tables, |
@@ -534,7 +534,7 @@ int mpx_handle_bd_fault(struct xregs_state *xsave_buf) | |||
534 | if (!kernel_managing_mpx_tables(current->mm)) | 534 | if (!kernel_managing_mpx_tables(current->mm)) |
535 | return -EINVAL; | 535 | return -EINVAL; |
536 | 536 | ||
537 | if (do_mpx_bt_fault(xsave_buf)) { | 537 | if (do_mpx_bt_fault(tsk)) { |
538 | force_sig(SIGSEGV, current); | 538 | force_sig(SIGSEGV, current); |
539 | /* | 539 | /* |
540 | * The force_sig() is essentially "handling" this | 540 | * The force_sig() is essentially "handling" this |