Commit 64d1304a64477629cb16b75491a77bafe6f86963
1 parent
279e677faa
Exists in
master
and in
4 other branches
futex: setup writeable mapping for futex ops which modify user space data
The futex code installs a read only mapping via get_user_pages_fast() even if the futex op function has to modify user space data. The eventual fault was fixed up by futex_handle_fault() which walked the VMA with mmap_sem held. After the cleanup patches which removed the mmap_sem dependency of the futex code commit 4dc5b7a36a49eff97050894cf1b3a9a02523717 (futex: clean up fault logic) removed the private VMA walk logic from the futex code. This change results in a stale RO mapping which is not fixed up. Instead of reintroducing the previous fault logic we set up the mapping in get_user_pages_fast() read/write for all operations which modify user space data. Also handle private futexes in the same way and make the current unconditional access_ok(VERIFY_WRITE) depend on the futex op. Reported-by: Andreas Schwab <schwab@linux-m68k.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> CC: stable@kernel.org
Showing 1 changed file with 13 additions and 11 deletions Side-by-side Diff
kernel/futex.c
... | ... | @@ -193,6 +193,7 @@ |
193 | 193 | * @uaddr: virtual address of the futex |
194 | 194 | * @fshared: 0 for a PROCESS_PRIVATE futex, 1 for PROCESS_SHARED |
195 | 195 | * @key: address where result is stored. |
196 | + * @rw: mapping needs to be read/write (values: VERIFY_READ, VERIFY_WRITE) | |
196 | 197 | * |
197 | 198 | * Returns a negative error code or 0 |
198 | 199 | * The key words are stored in *key on success. |
... | ... | @@ -203,7 +204,8 @@ |
203 | 204 | * |
204 | 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 | 210 | unsigned long address = (unsigned long)uaddr; |
209 | 211 | struct mm_struct *mm = current->mm; |
... | ... | @@ -226,7 +228,7 @@ |
226 | 228 | * but access_ok() should be faster than find_vma() |
227 | 229 | */ |
228 | 230 | if (!fshared) { |
229 | - if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))) | |
231 | + if (unlikely(!access_ok(rw, uaddr, sizeof(u32)))) | |
230 | 232 | return -EFAULT; |
231 | 233 | key->private.mm = mm; |
232 | 234 | key->private.address = address; |
... | ... | @@ -235,7 +237,7 @@ |
235 | 237 | } |
236 | 238 | |
237 | 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 | 241 | if (err < 0) |
240 | 242 | return err; |
241 | 243 | |
... | ... | @@ -677,7 +679,7 @@ |
677 | 679 | if (!bitset) |
678 | 680 | return -EINVAL; |
679 | 681 | |
680 | - ret = get_futex_key(uaddr, fshared, &key); | |
682 | + ret = get_futex_key(uaddr, fshared, &key, VERIFY_READ); | |
681 | 683 | if (unlikely(ret != 0)) |
682 | 684 | goto out; |
683 | 685 | |
684 | 686 | |
... | ... | @@ -723,10 +725,10 @@ |
723 | 725 | int ret, op_ret; |
724 | 726 | |
725 | 727 | retry: |
726 | - ret = get_futex_key(uaddr1, fshared, &key1); | |
728 | + ret = get_futex_key(uaddr1, fshared, &key1, VERIFY_READ); | |
727 | 729 | if (unlikely(ret != 0)) |
728 | 730 | goto out; |
729 | - ret = get_futex_key(uaddr2, fshared, &key2); | |
731 | + ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_WRITE); | |
730 | 732 | if (unlikely(ret != 0)) |
731 | 733 | goto out_put_key1; |
732 | 734 | |
733 | 735 | |
... | ... | @@ -814,10 +816,10 @@ |
814 | 816 | int ret, drop_count = 0; |
815 | 817 | |
816 | 818 | retry: |
817 | - ret = get_futex_key(uaddr1, fshared, &key1); | |
819 | + ret = get_futex_key(uaddr1, fshared, &key1, VERIFY_READ); | |
818 | 820 | if (unlikely(ret != 0)) |
819 | 821 | goto out; |
820 | - ret = get_futex_key(uaddr2, fshared, &key2); | |
822 | + ret = get_futex_key(uaddr2, fshared, &key2, VERIFY_READ); | |
821 | 823 | if (unlikely(ret != 0)) |
822 | 824 | goto out_put_key1; |
823 | 825 | |
... | ... | @@ -1140,7 +1142,7 @@ |
1140 | 1142 | q.bitset = bitset; |
1141 | 1143 | retry: |
1142 | 1144 | q.key = FUTEX_KEY_INIT; |
1143 | - ret = get_futex_key(uaddr, fshared, &q.key); | |
1145 | + ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_READ); | |
1144 | 1146 | if (unlikely(ret != 0)) |
1145 | 1147 | goto out; |
1146 | 1148 | |
... | ... | @@ -1330,7 +1332,7 @@ |
1330 | 1332 | q.pi_state = NULL; |
1331 | 1333 | retry: |
1332 | 1334 | q.key = FUTEX_KEY_INIT; |
1333 | - ret = get_futex_key(uaddr, fshared, &q.key); | |
1335 | + ret = get_futex_key(uaddr, fshared, &q.key, VERIFY_WRITE); | |
1334 | 1336 | if (unlikely(ret != 0)) |
1335 | 1337 | goto out; |
1336 | 1338 | |
... | ... | @@ -1594,7 +1596,7 @@ |
1594 | 1596 | if ((uval & FUTEX_TID_MASK) != task_pid_vnr(current)) |
1595 | 1597 | return -EPERM; |
1596 | 1598 | |
1597 | - ret = get_futex_key(uaddr, fshared, &key); | |
1599 | + ret = get_futex_key(uaddr, fshared, &key, VERIFY_WRITE); | |
1598 | 1600 | if (unlikely(ret != 0)) |
1599 | 1601 | goto out; |
1600 | 1602 |