diff options
Diffstat (limited to 'kernel/futex.c')
| -rw-r--r-- | kernel/futex.c | 60 |
1 files changed, 45 insertions, 15 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index fe28dc282eae..11cbe052b2e8 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
| @@ -218,6 +218,8 @@ static void drop_futex_key_refs(union futex_key *key) | |||
| 218 | * @uaddr: virtual address of the futex | 218 | * @uaddr: virtual address of the futex |
| 219 | * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED | 219 | * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED |
| 220 | * @key: address where result is stored. | 220 | * @key: address where result is stored. |
| 221 | * @rw: mapping needs to be read/write (values: VERIFY_READ, | ||
| 222 | * VERIFY_WRITE) | ||
| 221 | * | 223 | * |
| 222 | * Returns a negative error code or 0 | 224 | * Returns a negative error code or 0 |
| 223 | * The key words are stored in *key on success. | 225 | * The key words are stored in *key on success. |
| @@ -229,12 +231,12 @@ static void drop_futex_key_refs(union futex_key *key) | |||
| 229 | * lock_page() might sleep, the caller should not hold a spinlock. | 231 | * lock_page() might sleep, the caller should not hold a spinlock. |
| 230 | */ | 232 | */ |
| 231 | static int | 233 | static int |
| 232 | get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) | 234 | get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) |
| 233 | { | 235 | { |
| 234 | unsigned long address = (unsigned long)uaddr; | 236 | unsigned long address = (unsigned long)uaddr; |
| 235 | struct mm_struct *mm = current->mm; | 237 | struct mm_struct *mm = current->mm; |
| 236 | struct page *page, *page_head; | 238 | struct page *page, *page_head; |
| 237 | int err; | 239 | int err, ro = 0; |
| 238 | 240 | ||
| 239 | /* | 241 | /* |
| 240 | * The futex address must be "naturally" aligned. | 242 | * The futex address must be "naturally" aligned. |
| @@ -262,8 +264,18 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) | |||
| 262 | 264 | ||
| 263 | again: | 265 | again: |
| 264 | err = get_user_pages_fast(address, 1, 1, &page); | 266 | err = get_user_pages_fast(address, 1, 1, &page); |
| 267 | /* | ||
| 268 | * If write access is not required (eg. FUTEX_WAIT), try | ||
| 269 | * and get read-only access. | ||
| 270 | */ | ||
| 271 | if (err == -EFAULT && rw == VERIFY_READ) { | ||
| 272 | err = get_user_pages_fast(address, 1, 0, &page); | ||
| 273 | ro = 1; | ||
| 274 | } | ||
| 265 | if (err < 0) | 275 | if (err < 0) |
| 266 | return err; | 276 | return err; |
| 277 | else | ||
| 278 | err = 0; | ||
| 267 | 279 | ||
| 268 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 280 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
| 269 | page_head = page; | 281 | page_head = page; |
| @@ -305,6 +317,13 @@ again: | |||
| 305 | if (!page_head->mapping) { | 317 | if (!page_head->mapping) { |
| 306 | unlock_page(page_head); | 318 | unlock_page(page_head); |
| 307 | put_page(page_head); | 319 | put_page(page_head); |
| 320 | /* | ||
| 321 | * ZERO_PAGE pages don't have a mapping. Avoid a busy loop | ||
| 322 | * trying to find one. RW mapping would have COW'd (and thus | ||
| 323 | * have a mapping) so this page is RO and won't ever change. | ||
| 324 | */ | ||
| 325 | if ((page_head == ZERO_PAGE(address))) | ||
| 326 | return -EFAULT; | ||
| 308 | goto again; | 327 | goto again; |
| 309 | } | 328 | } |
| 310 | 329 | ||
| @@ -316,6 +335,15 @@ again: | |||
| 316 | * the object not the particular process. | 335 | * the object not the particular process. |
| 317 | */ | 336 | */ |
| 318 | if (PageAnon(page_head)) { | 337 | if (PageAnon(page_head)) { |
| 338 | /* | ||
| 339 | * A RO anonymous page will never change and thus doesn't make | ||
| 340 | * sense for futex operations. | ||
| 341 | */ | ||
| 342 | if (ro) { | ||
| 343 | err = -EFAULT; | ||
| 344 | goto out; | ||
| 345 | } | ||
| 346 | |||
| 319 | key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */ | 347 | key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */ |
| 320 | key->private.mm = mm; | 348 | key->private.mm = mm; |
| 321 | key->private.address = address; | 349 | key->private.address = address; |
| @@ -327,9 +355,10 @@ again: | |||
| 327 | 355 | ||
| 328 | get_futex_key_refs(key); | 356 | get_futex_key_refs(key); |
| 329 | 357 | ||
| 358 | out: | ||
| 330 | unlock_page(page_head); | 359 | unlock_page(page_head); |
| 331 | put_page(page_head); | 360 | put_page(page_head); |
| 332 | return 0; | 361 | return err; |
| 333 | } | 362 | } |
| 334 | 363 | ||
| 335 | static inline void put_futex_key(union futex_key *key) | 364 | static inline void put_futex_key(union futex_key *key) |
| @@ -355,8 +384,8 @@ static int fault_in_user_writeable(u32 __user *uaddr) | |||
| 355 | int ret; | 384 | int ret; |
| 356 | 385 | ||
| 357 | down_read(&mm->mmap_sem); | 386 | down_read(&mm->mmap_sem); |
| 358 | ret = get_user_pages(current, mm, (unsigned long)uaddr, | 387 | ret = fixup_user_fault(current, mm, (unsigned long)uaddr, |
| 359 | 1, 1, 0, NULL, NULL); | 388 | FAULT_FLAG_WRITE); |
| 360 | up_read(&mm->mmap_sem); | 389 | up_read(&mm->mmap_sem); |
| 361 | 390 | ||
| 362 | return ret < 0 ? ret : 0; | 391 | return ret < 0 ? ret : 0; |
| @@ -940,7 +969,7 @@ futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset) | |||
| 940 | if (!bitset) | 969 | if (!bitset) |
| 941 | return -EINVAL; | 970 | return -EINVAL; |
| 942 | 971 | ||
| 943 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key); | 972 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_READ); |
| 944 | if (unlikely(ret != 0)) | 973 | if (unlikely(ret != 0)) |
| 945 | goto out; | 974 | goto out; |
| 946 | 975 | ||
| @@ -986,10 +1015,10 @@ futex_wake_op(u32 __user *uaddr1, unsigned int flags, u32 __user *uaddr2, | |||
| 986 | int ret, op_ret; | 1015 | int ret, op_ret; |
| 987 | 1016 | ||
| 988 | retry: | 1017 | retry: |
| 989 | ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1); | 1018 | ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ); |
| 990 | if (unlikely(ret != 0)) | 1019 | if (unlikely(ret != 0)) |
| 991 | goto out; | 1020 | goto out; |
| 992 | ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2); | 1021 | ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE); |
| 993 | if (unlikely(ret != 0)) | 1022 | if (unlikely(ret != 0)) |
| 994 | goto out_put_key1; | 1023 | goto out_put_key1; |
| 995 | 1024 | ||
| @@ -1243,10 +1272,11 @@ retry: | |||
| 1243 | pi_state = NULL; | 1272 | pi_state = NULL; |
| 1244 | } | 1273 | } |
| 1245 | 1274 | ||
| 1246 | ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1); | 1275 | ret = get_futex_key(uaddr1, flags & FLAGS_SHARED, &key1, VERIFY_READ); |
| 1247 | if (unlikely(ret != 0)) | 1276 | if (unlikely(ret != 0)) |
| 1248 | goto out; | 1277 | goto out; |
| 1249 | ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2); | 1278 | ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, |
| 1279 | requeue_pi ? VERIFY_WRITE : VERIFY_READ); | ||
| 1250 | if (unlikely(ret != 0)) | 1280 | if (unlikely(ret != 0)) |
| 1251 | goto out_put_key1; | 1281 | goto out_put_key1; |
| 1252 | 1282 | ||
| @@ -1790,7 +1820,7 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags, | |||
| 1790 | * while the syscall executes. | 1820 | * while the syscall executes. |
| 1791 | */ | 1821 | */ |
| 1792 | retry: | 1822 | retry: |
| 1793 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key); | 1823 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ); |
| 1794 | if (unlikely(ret != 0)) | 1824 | if (unlikely(ret != 0)) |
| 1795 | return ret; | 1825 | return ret; |
| 1796 | 1826 | ||
| @@ -1941,7 +1971,7 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags, int detect, | |||
| 1941 | } | 1971 | } |
| 1942 | 1972 | ||
| 1943 | retry: | 1973 | retry: |
| 1944 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key); | 1974 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q.key, VERIFY_WRITE); |
| 1945 | if (unlikely(ret != 0)) | 1975 | if (unlikely(ret != 0)) |
| 1946 | goto out; | 1976 | goto out; |
| 1947 | 1977 | ||
| @@ -2060,7 +2090,7 @@ retry: | |||
| 2060 | if ((uval & FUTEX_TID_MASK) != vpid) | 2090 | if ((uval & FUTEX_TID_MASK) != vpid) |
| 2061 | return -EPERM; | 2091 | return -EPERM; |
| 2062 | 2092 | ||
| 2063 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key); | 2093 | ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, VERIFY_WRITE); |
| 2064 | if (unlikely(ret != 0)) | 2094 | if (unlikely(ret != 0)) |
| 2065 | goto out; | 2095 | goto out; |
| 2066 | 2096 | ||
| @@ -2249,7 +2279,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags, | |||
| 2249 | debug_rt_mutex_init_waiter(&rt_waiter); | 2279 | debug_rt_mutex_init_waiter(&rt_waiter); |
| 2250 | rt_waiter.task = NULL; | 2280 | rt_waiter.task = NULL; |
| 2251 | 2281 | ||
| 2252 | ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2); | 2282 | ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE); |
| 2253 | if (unlikely(ret != 0)) | 2283 | if (unlikely(ret != 0)) |
| 2254 | goto out; | 2284 | goto out; |
| 2255 | 2285 | ||
| @@ -2697,7 +2727,7 @@ static int __init futex_init(void) | |||
| 2697 | futex_cmpxchg_enabled = 1; | 2727 | futex_cmpxchg_enabled = 1; |
| 2698 | 2728 | ||
| 2699 | for (i = 0; i < ARRAY_SIZE(futex_queues); i++) { | 2729 | for (i = 0; i < ARRAY_SIZE(futex_queues); i++) { |
| 2700 | plist_head_init(&futex_queues[i].chain, &futex_queues[i].lock); | 2730 | plist_head_init(&futex_queues[i].chain); |
| 2701 | spin_lock_init(&futex_queues[i].lock); | 2731 | spin_lock_init(&futex_queues[i].lock); |
| 2702 | } | 2732 | } |
| 2703 | 2733 | ||
