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>
This commit is contained in:
parent
5458765390
commit
6ac52bb491
|
@ -419,6 +419,35 @@ int mpx_disable_management(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mpx_cmpxchg_bd_entry(struct mm_struct *mm,
|
||||||
|
unsigned long *curval,
|
||||||
|
unsigned long __user *addr,
|
||||||
|
unsigned long old_val, unsigned long new_val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
/*
|
||||||
|
* user_atomic_cmpxchg_inatomic() actually uses sizeof()
|
||||||
|
* the pointer that we pass to it to figure out how much
|
||||||
|
* data to cmpxchg. We have to be careful here not to
|
||||||
|
* pass a pointer to a 64-bit data type when we only want
|
||||||
|
* a 32-bit copy.
|
||||||
|
*/
|
||||||
|
if (is_64bit_mm(mm)) {
|
||||||
|
ret = user_atomic_cmpxchg_inatomic(curval,
|
||||||
|
addr, old_val, new_val);
|
||||||
|
} else {
|
||||||
|
u32 uninitialized_var(curval_32);
|
||||||
|
u32 old_val_32 = old_val;
|
||||||
|
u32 new_val_32 = new_val;
|
||||||
|
u32 __user *addr_32 = (u32 __user *)addr;
|
||||||
|
|
||||||
|
ret = user_atomic_cmpxchg_inatomic(&curval_32,
|
||||||
|
addr_32, old_val_32, new_val_32);
|
||||||
|
*curval = curval_32;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* With 32-bit mode, MPX_BT_SIZE_BYTES is 4MB, and the size of each
|
* With 32-bit mode, MPX_BT_SIZE_BYTES is 4MB, and the size of each
|
||||||
* bounds table is 16KB. With 64-bit mode, MPX_BT_SIZE_BYTES is 2GB,
|
* bounds table is 16KB. With 64-bit mode, MPX_BT_SIZE_BYTES is 2GB,
|
||||||
|
@ -426,6 +455,7 @@ int mpx_disable_management(void)
|
||||||
*/
|
*/
|
||||||
static int allocate_bt(long __user *bd_entry)
|
static int allocate_bt(long __user *bd_entry)
|
||||||
{
|
{
|
||||||
|
struct mm_struct *mm = current->mm;
|
||||||
unsigned long expected_old_val = 0;
|
unsigned long expected_old_val = 0;
|
||||||
unsigned long actual_old_val = 0;
|
unsigned long actual_old_val = 0;
|
||||||
unsigned long bt_addr;
|
unsigned long bt_addr;
|
||||||
|
@ -455,7 +485,7 @@ static int allocate_bt(long __user *bd_entry)
|
||||||
* mmap_sem at this point, unlike some of the other part
|
* mmap_sem at this point, unlike some of the other part
|
||||||
* of the MPX code that have to pagefault_disable().
|
* of the MPX code that have to pagefault_disable().
|
||||||
*/
|
*/
|
||||||
ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry,
|
ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val, bd_entry,
|
||||||
expected_old_val, bd_new_entry);
|
expected_old_val, bd_new_entry);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unmap;
|
goto out_unmap;
|
||||||
|
@ -710,15 +740,16 @@ static int unmap_single_bt(struct mm_struct *mm,
|
||||||
long __user *bd_entry, unsigned long bt_addr)
|
long __user *bd_entry, unsigned long bt_addr)
|
||||||
{
|
{
|
||||||
unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG;
|
unsigned long expected_old_val = bt_addr | MPX_BD_ENTRY_VALID_FLAG;
|
||||||
unsigned long actual_old_val = 0;
|
unsigned long uninitialized_var(actual_old_val);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int need_write = 1;
|
int need_write = 1;
|
||||||
|
unsigned long cleared_bd_entry = 0;
|
||||||
|
|
||||||
pagefault_disable();
|
pagefault_disable();
|
||||||
ret = user_atomic_cmpxchg_inatomic(&actual_old_val, bd_entry,
|
ret = mpx_cmpxchg_bd_entry(mm, &actual_old_val,
|
||||||
expected_old_val, 0);
|
bd_entry, expected_old_val, cleared_bd_entry);
|
||||||
pagefault_enable();
|
pagefault_enable();
|
||||||
if (!ret)
|
if (!ret)
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue