diff options
Diffstat (limited to 'kernel/futex.c')
| -rw-r--r-- | kernel/futex.c | 54 | 
1 files changed, 42 insertions, 12 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 557c9c7cdd8..1511dff0cfd 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -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;  |