diff options
Diffstat (limited to 'kernel/futex.c')
-rw-r--r-- | kernel/futex.c | 31 |
1 files changed, 19 insertions, 12 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 6b50a024bca2..d546b2d53a62 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -193,6 +193,7 @@ static void drop_futex_key_refs(union futex_key *key) | |||
193 | * @uaddr: virtual address of the futex | 193 | * @uaddr: virtual address of the futex |
194 | * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED | 194 | * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED |
195 | * @key: address where result is stored. | 195 | * @key: address where result is stored. |
196 | * @rw: mapping needs to be read/write (values: VERIFY_READ, VERIFY_WRITE) | ||
196 | * | 197 | * |
197 | * Returns a negative error code or 0 | 198 | * Returns a negative error code or 0 |
198 | * The key words are stored in *key on success. | 199 | * The key words are stored in *key on success. |
@@ -203,7 +204,8 @@ static void drop_futex_key_refs(union futex_key *key) | |||
203 | * | 204 | * |
204 | * lock_page() might sleep, the caller should not hold a spinlock. | 205 | * lock_page() might sleep, the caller should not hold a spinlock. |
205 | */ | 206 | */ |
206 | static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) | 207 | static int |
208 | get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) | ||
207 | { | 209 | { |
208 | unsigned long address = (unsigned long)uaddr; | 210 | unsigned long address = (unsigned long)uaddr; |
209 | struct mm_struct *mm = current->mm; | 211 | struct mm_struct *mm = current->mm; |
@@ -226,7 +228,7 @@ static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) | |||
226 | * but access_ok() should be faster than find_vma() | 228 | * but access_ok() should be faster than find_vma() |
227 | */ | 229 | */ |
228 | if (!fshared) { | 230 | if (!fshared) { |
229 | if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))) | 231 | if (unlikely(!access_ok(rw, uaddr, sizeof(u32)))) |
230 | return -EFAULT; | 232 | return -EFAULT; |
231 | key->private.mm = mm; | 233 | key->private.mm = mm; |
232 | key->private.address = address; | 234 | key->private.address = address; |
@@ -235,7 +237,7 @@ static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) | |||
235 | } | 237 | } |
236 | 238 | ||
237 | again: | 239 | again: |
238 | err = get_user_pages_fast(address, 1, 0, &page); | 240 | err = get_user_pages_fast(address, 1, rw == VERIFY_WRITE, &page); |
239 | if (err < 0) | 241 | if (err < 0) |
240 | return err; | 242 | return err; |
241 | 243 | ||
@@ -677,7 +679,7 @@ static int futex_wake(u32 __user *uaddr, int fshared, int nr_wake, u32 bitset) | |||
677 | if (!bitset) | 679 | if (!bitset) |
678 | return -EINVAL; | 680 | return -EINVAL; |
679 | 681 | ||
680 | ret = get_futex_key(uaddr, fshared, &key); | 682 | ret = get_futex_key(uaddr, fshared, &key, VERIFY_READ); |
681 | if (unlikely(ret != 0)) | 683 | if (unlikely(ret != 0)) |
682 | goto out; | 684 | goto out; |
683 | 685 | ||
@@ -723,10 +725,10 @@ futex_wake_op(u32 __user *uaddr1, int fshared, u32 __user *uaddr2, | |||
723 | int ret, op_ret; | 725 | int ret, op_ret; |
724 | 726 | ||
725 | retry: | 727 | retry: |
726 | ret = get_futex_key(uaddr1, fshared, &key1); | 728 | ret = get_futex_key(uaddr1, fshared, &key1, VERIFY_READ); |
727 | if (unlikely(ret != 0)) | 729 | if (unlikely(ret != 0)) |
728 | goto out; | 730 | goto out; |
729 | ret = get_futex_key(uaddr2, fshared, &key2); | 731 | ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_WRITE); |
730 | if (unlikely(ret != 0)) | 732 | if (unlikely(ret != 0)) |
731 | goto out_put_key1; | 733 | goto out_put_key1; |
732 | 734 | ||
@@ -814,10 +816,10 @@ static int futex_requeue(u32 __user *uaddr1, int fshared, u32 __user *uaddr2, | |||
814 | int ret, drop_count = 0; | 816 | int ret, drop_count = 0; |
815 | 817 | ||
816 | retry: | 818 | retry: |
817 | ret = get_futex_key(uaddr1, fshared, &key1); | 819 | ret = get_futex_key(uaddr1, fshared, &key1, VERIFY_READ); |
818 | if (unlikely(ret != 0)) | 820 | if (unlikely(ret != 0)) |
819 | goto out; | 821 | goto out; |
820 | ret = get_futex_key(uaddr2, fshared, &key2); | 822 | ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_READ); |
821 | if (unlikely(ret != 0)) | 823 | if (unlikely(ret != 0)) |
822 | goto out_put_key1; | 824 | goto out_put_key1; |
823 | 825 | ||
@@ -883,7 +885,12 @@ retry_private: | |||
883 | out_unlock: | 885 | out_unlock: |
884 | double_unlock_hb(hb1, hb2); | 886 | double_unlock_hb(hb1, hb2); |
885 | 887 | ||
886 | /* drop_futex_key_refs() must be called outside the spinlocks. */ | 888 | /* |
889 | * drop_futex_key_refs() must be called outside the spinlocks. During | ||
890 | * the requeue we moved futex_q's from the hash bucket at key1 to the | ||
891 | * one at key2 and updated their key pointer. We no longer need to | ||
892 | * hold the references to key1. | ||
893 | */ | ||
887 | while (--drop_count >= 0) | 894 | while (--drop_count >= 0) |
888 | drop_futex_key_refs(&key1); | 895 | drop_futex_key_refs(&key1); |
889 | 896 | ||
@@ -1135,7 +1142,7 @@ static int futex_wait(u32 __user *uaddr, int fshared, | |||
1135 | q.bitset = bitset; | 1142 | q.bitset = bitset; |
1136 | retry: | 1143 | retry: |
1137 | q.key = FUTEX_KEY_INIT; | 1144 | q.key = FUTEX_KEY_INIT; |
1138 | ret = get_futex_key(uaddr, fshared, &q.key); | 1145 | ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_READ); |
1139 | if (unlikely(ret != 0)) | 1146 | if (unlikely(ret != 0)) |
1140 | goto out; | 1147 | goto out; |
1141 | 1148 | ||
@@ -1325,7 +1332,7 @@ static int futex_lock_pi(u32 __user *uaddr, int fshared, | |||
1325 | q.pi_state = NULL; | 1332 | q.pi_state = NULL; |
1326 | retry: | 1333 | retry: |
1327 | q.key = FUTEX_KEY_INIT; | 1334 | q.key = FUTEX_KEY_INIT; |
1328 | ret = get_futex_key(uaddr, fshared, &q.key); | 1335 | ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_WRITE); |
1329 | if (unlikely(ret != 0)) | 1336 | if (unlikely(ret != 0)) |
1330 | goto out; | 1337 | goto out; |
1331 | 1338 | ||
@@ -1589,7 +1596,7 @@ retry: | |||
1589 | if ((uval & FUTEX_TID_MASK) != task_pid_vnr(current)) | 1596 | if ((uval & FUTEX_TID_MASK) != task_pid_vnr(current)) |
1590 | return -EPERM; | 1597 | return -EPERM; |
1591 | 1598 | ||
1592 | ret = get_futex_key(uaddr, fshared, &key); | 1599 | ret = get_futex_key(uaddr, fshared, &key, VERIFY_WRITE); |
1593 | if (unlikely(ret != 0)) | 1600 | if (unlikely(ret != 0)) |
1594 | goto out; | 1601 | goto out; |
1595 | 1602 | ||