aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Hansen <dave.hansen@linux.intel.com>2015-06-07 14:37:05 -0400
committerIngo Molnar <mingo@kernel.org>2015-06-09 06:24:33 -0400
commit6ac52bb4913eadfa327138b91aab5d37234a2c3b (patch)
tree0694f692896dfbc62c172b4d00ff4d2d8d4aac27
parent54587653904c552c56b9dec153d7a89063394b09 (diff)
x86/mpx: Use 32-bit-only cmpxchg() for 32-bit apps
user_atomic_cmpxchg_inatomic() actually looks at sizeof(*ptr) to figure out how many bytes to copy. If we run it on a 64-bit kernel with a 64-bit pointer, it will copy a 64-bit bounds directory entry. That's fine, except when we have 32-bit programs with 32-bit bounds directory entries and we only *want* 32-bits. This patch breaks the cmpxchg() operation out in to its own function and performs the 32-bit type swizzling in there. Note, the "64-bit" version of this code _would_ work on a 32-bit-only kernel. The issue this patch addresses is only for when the kernel's 'long' is mismatched from the size of the bounds directory entry of the process we are working on. The new helper modifies 'actual_old_val' or returns an error. But gcc doesn't know this, so it warns about 'actual_old_val' being unused. Shut it up with an uninitialized_var(). 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: Dave Hansen <dave@sr71.net> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/20150607183705.672B115E@viggo.jf.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/mm/mpx.c41
1 files changed, 36 insertions, 5 deletions
diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c
index 8cc793479039..294ea2092ef5 100644
--- a/arch/x86/mm/mpx.c
+++ b/arch/x86/mm/mpx.c
@@ -419,6 +419,35 @@ int mpx_disable_management(void)
419 return 0; 419 return 0;
420} 420}
421 421
422static int mpx_cmpxchg_bd_entry(struct mm_struct *mm,
423 unsigned long *curval,
424 unsigned long __user *addr,
425 unsigned long old_val, unsigned long new_val)
426{
427 int ret;
428 /*
429 * user_atomic_cmpxchg_inatomic() actually uses sizeof()
430 * the pointer that we pass to it to figure out how much
431 * data to cmpxchg. We have to be careful here not to
432 * pass a pointer to a 64-bit data type when we only want
433 * a 32-bit copy.
434 */
435 if (is_64bit_mm(mm)) {
436 ret = user_atomic_cmpxchg_inatomic(curval,
437 addr, old_val, new_val);
438 } else {
439 u32 uninitialized_var(curval_32);
440 u32 old_val_32 = old_val;
441 u32 new_val_32 = new_val;
442 u32 __user *addr_32 = (u32 __user *)addr;
443
444 ret = user_atomic_cmpxchg_inatomic(&curval_32,
445 addr_32, old_val_32, new_val_32);
446 *curval = curval_32;
447 }
448 return ret;
449}
450
422/* 451/*
423 * With 32-bit mode, MPX_BT_SIZE_BYTES is 4MB, and the size of each 452 * With 32-bit mode, MPX_BT_SIZE_BYTES is 4MB, and the size of each
424 * bounds table is 16KB. With 64-bit mode, MPX_BT_SIZE_BYTES is 2GB, 453 * bounds table is 16KB. With 64-bit mode, MPX_BT_SIZE_BYTES is 2GB,
@@ -426,6 +455,7 @@ int mpx_disable_management(void)
426 */ 455 */
427static int allocate_bt(long __user *bd_entry) 456static int allocate_bt(long __user *bd_entry)
428{ 457{
458 struct mm_struct *mm = current->mm;
429 unsigned long expected_old_val = 0; 459 unsigned long expected_old_val = 0;
430 unsigned long actual_old_val = 0; 460 unsigned long actual_old_val = 0;
431 unsigned long bt_addr; 461 unsigned long bt_addr;
@@ -455,8 +485,8 @@ static int allocate_bt(long __user *bd_entry)
455 * mmap_sem at this point, unlike some of the other part 485 * mmap_sem at this point, unlike some of the other part
456 * of the MPX code that have to pagefault_disable(). 486 * of the MPX code that have to pagefault_disable().
457 */ 487 */
458 ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry, 488 ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val, bd_entry,
459 expected_old_val, bd_new_entry); 489 expected_old_val, bd_new_entry);
460 if (ret) 490 if (ret)
461 goto out_unmap; 491 goto out_unmap;
462 492
@@ -710,15 +740,16 @@ static int unmap_single_bt(struct mm_struct *mm,
710 long __user *bd_entry, unsigned long bt_addr) 740 long __user *bd_entry, unsigned long bt_addr)
711{ 741{
712 unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG; 742 unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG;
713 unsigned long actual_old_val = 0; 743 unsigned long uninitialized_var(actual_old_val);
714 int ret; 744 int ret;
715 745
716 while (1) { 746 while (1) {
717 int need_write = 1; 747 int need_write = 1;
748 unsigned long cleared_bd_entry = 0;
718 749
719 pagefault_disable(); 750 pagefault_disable();
720 ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry, 751 ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val,
721 expected_old_val, 0); 752 bd_entry, expected_old_val, cleared_bd_entry);
722 pagefault_enable(); 753 pagefault_enable();
723 if (!ret) 754 if (!ret)
724 break; 755 break;