|
@@ -218,6 +218,8 @@ static void drop_futex_key_refs(union futex_key *key)
|
|
|
* @uaddr: virtual address of the futex
|
|
|
* @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED
|
|
|
* @key: address where result is stored.
|
|
|
+ * @rw: mapping needs to be read/write (values: VERIFY_READ,
|
|
|
+ * VERIFY_WRITE)
|
|
|
*
|
|
|
* Returns a negative error code or 0
|
|
|
* The key words are stored in *key on success.
|
|
@@ -229,12 +231,12 @@ static void drop_futex_key_refs(union futex_key *key)
|
|
|
* lock_page() might sleep, the caller should not hold a spinlock.
|
|
|
*/
|
|
|
static int
|
|
|
-get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
|
|
|
+get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw)
|
|
|
{
|
|
|
unsigned long address = (unsigned long)uaddr;
|
|
|
struct mm_struct *mm = current->mm;
|
|
|
struct page *page, *page_head;
|
|
|
- int err;
|
|
|
+ int err, ro = 0;
|
|
|
|
|
|
/*
|
|
|
* The futex address must be "naturally" aligned.
|
|
@@ -262,8 +264,18 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
|
|
|
|
|
|
again:
|
|
|
err = get_user_pages_fast(address, 1, 1, &page);
|
|
|
+ /*
|
|
|
+ * If write access is not required (eg. FUTEX_WAIT), try
|
|
|
+ * and get read-only access.
|
|
|
+ */
|
|
|
+ if (err == -EFAULT && rw == VERIFY_READ) {
|
|
|
+ err = get_user_pages_fast(address, 1, 0, &page);
|
|
|
+ ro = 1;
|
|
|
+ }
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
+ else
|
|
|
+ err = 0;
|
|
|
|
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
|
page_head = page;
|
|
@@ -305,6 +317,13 @@ again:
|
|
|
if (!page_head->mapping) {
|
|
|
unlock_page(page_head);
|
|
|
put_page(page_head);
|
|
|
+ /*
|
|
|
+ * ZERO_PAGE pages don't have a mapping. Avoid a busy loop
|
|
|
+ * trying to find one. RW mapping would have COW'd (and thus
|
|
|
+ * have a mapping) so this page is RO and won't ever change.
|
|
|
+ */
|
|
|
+ if ((page_head == ZERO_PAGE(address)))
|
|
|
+ return -EFAULT;
|
|
|
goto again;
|
|
|
}
|
|
|
|
|
@@ -316,6 +335,15 @@ again:
|
|
|
* the object not the particular process.
|
|
|
*/
|
|
|
if (PageAnon(page_head)) {
|
|
|
+ /*
|
|
|
+ * A RO anonymous page will never change and thus doesn't make
|
|
|
+ * sense for futex operations.
|
|
|
+ */
|
|
|
+ if (ro) {
|
|
|
+ err = -EFAULT;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */
|
|
|
key->private.mm = mm;
|
|
|
key->private.address = address;
|
|
@@ -327,9 +355,10 @@ again:
|
|
|
|
|
|
get_futex_key_refs(key);
|
|
|
|
|
|
+out:
|
|
|
unlock_page(page_head);
|
|
|
put_page(page_head);
|
|
|
- return 0;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static inline void put_futex_key(union futex_key *key)
|
|
@@ -940,7 +969,7 @@ futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
|
|
|
if (!bitset)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
|
|
|
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_READ);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out;
|
|
|
|
|
@@ -986,10 +1015,10 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2,
|
|
|
int ret, op_ret;
|
|
|
|
|
|
retry:
|
|
|
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
|
|
|
+ ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out;
|
|
|
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
|
|
|
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out_put_key1;
|
|
|
|
|
@@ -1243,10 +1272,11 @@ retry:
|
|
|
pi_state = NULL;
|
|
|
}
|
|
|
|
|
|
- ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1);
|
|
|
+ ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out;
|
|
|
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
|
|
|
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2,
|
|
|
+ requeue_pi ? VERIFY_WRITE : VERIFY_READ);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out_put_key1;
|
|
|
|
|
@@ -1790,7 +1820,7 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
|
|
|
* while the syscall executes.
|
|
|
*/
|
|
|
retry:
|
|
|
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key);
|
|
|
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ);
|
|
|
if (unlikely(ret != 0))
|
|
|
return ret;
|
|
|
|
|
@@ -1941,7 +1971,7 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags, int detect,
|
|
|
}
|
|
|
|
|
|
retry:
|
|
|
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key);
|
|
|
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key, VERIFY_WRITE);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out;
|
|
|
|
|
@@ -2060,7 +2090,7 @@ retry:
|
|
|
if ((uval & FUTEX_TID_MASK) != vpid)
|
|
|
return -EPERM;
|
|
|
|
|
|
- ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key);
|
|
|
+ ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_WRITE);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out;
|
|
|
|
|
@@ -2249,7 +2279,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
|
|
debug_rt_mutex_init_waiter(&rt_waiter);
|
|
|
rt_waiter.task = NULL;
|
|
|
|
|
|
- ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2);
|
|
|
+ ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
|
|
|
if (unlikely(ret != 0))
|
|
|
goto out;
|
|
|
|