Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Dave Hansen and committed by
Ingo Molnar
6ac52bb4 54587653

+36 -5
+36 -5
arch/x86/mm/mpx.c
··· 419 419 return 0; 420 420 } 421 421 422 + static 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 452 * With 32-bit mode, MPX_BT_SIZE_BYTES is 4MB, and the size of each 424 453 * bounds table is 16KB. With 64-bit mode, MPX_BT_SIZE_BYTES is 2GB, ··· 455 426 */ 456 427 static int allocate_bt(long __user *bd_entry) 457 428 { 429 + struct mm_struct *mm = current->mm; 458 430 unsigned long expected_old_val = 0; 459 431 unsigned long actual_old_val = 0; 460 432 unsigned long bt_addr; ··· 485 455 * mmap_sem at this point, unlike some of the other part 486 456 * of the MPX code that have to pagefault_disable(). 487 457 */ 488 - ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry, 489 - expected_old_val, bd_new_entry); 458 + ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val, bd_entry, 459 + expected_old_val, bd_new_entry); 490 460 if (ret) 491 461 goto out_unmap; 492 462 ··· 740 710 long __user *bd_entry, unsigned long bt_addr) 741 711 { 742 712 unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG; 743 - unsigned long actual_old_val = 0; 713 + unsigned long uninitialized_var(actual_old_val); 744 714 int ret; 745 715 746 716 while (1) { 747 717 int need_write = 1; 718 + unsigned long cleared_bd_entry = 0; 748 719 749 720 pagefault_disable(); 750 - ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry, 751 - expected_old_val, 0); 721 + ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val, 722 + bd_entry, expected_old_val, cleared_bd_entry); 752 723 pagefault_enable(); 753 724 if (!ret) 754 725 break;