diff options
author | Michel Lespinasse <walken@google.com> | 2011-03-10 21:48:51 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2011-03-11 06:23:08 -0500 |
commit | 37a9d912b24f96a0591773e6e6c3642991ae5a70 (patch) | |
tree | 5c4d5b9a52e2c533269e589115afbd25b6c8534b /kernel/futex.c | |
parent | 522d7decc0370070448a8c28982c8dfd8970489e (diff) |
futex: Sanitize cmpxchg_futex_value_locked API
The cmpxchg_futex_value_locked API was funny in that it returned either
the original, user-exposed futex value OR an error code such as -EFAULT.
This was confusing at best, and could be a source of livelocks in places
that retry the cmpxchg_futex_value_locked after trying to fix the issue
by running fault_in_user_writeable().
This change makes the cmpxchg_futex_value_locked API more similar to the
get_futex_value_locked one, returning an error code and updating the
original value through a reference argument.
Signed-off-by: Michel Lespinasse <walken@google.com>
Acked-by: Chris Metcalf <cmetcalf@tilera.com> [tile]
Acked-by: Tony Luck <tony.luck@intel.com> [ia64]
Acked-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Michal Simek <monstr@monstr.eu> [microblaze]
Acked-by: David Howells <dhowells@redhat.com> [frv]
Cc: Darren Hart <darren@dvhart.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Matt Turner <mattst88@gmail.com>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
LKML-Reference: <20110311024851.GC26122@google.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/futex.c')
-rw-r--r-- | kernel/futex.c | 45 |
1 files changed, 15 insertions, 30 deletions
diff --git a/kernel/futex.c b/kernel/futex.c index 773815465bac..237f14bfc022 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -381,15 +381,16 @@ static struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, | |||
381 | return NULL; | 381 | return NULL; |
382 | } | 382 | } |
383 | 383 | ||
384 | static u32 cmpxchg_futex_value_locked(u32 __user *uaddr, u32 uval, u32 newval) | 384 | static int cmpxchg_futex_value_locked(u32 *curval, u32 __user *uaddr, |
385 | u32 uval, u32 newval) | ||
385 | { | 386 | { |
386 | u32 curval; | 387 | int ret; |
387 | 388 | ||
388 | pagefault_disable(); | 389 | pagefault_disable(); |
389 | curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); | 390 | ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); |
390 | pagefault_enable(); | 391 | pagefault_enable(); |
391 | 392 | ||
392 | return curval; | 393 | return ret; |
393 | } | 394 | } |
394 | 395 | ||
395 | static int get_futex_value_locked(u32 *dest, u32 __user *from) | 396 | static int get_futex_value_locked(u32 *dest, u32 __user *from) |
@@ -688,9 +689,7 @@ retry: | |||
688 | if (set_waiters) | 689 | if (set_waiters) |
689 | newval |= FUTEX_WAITERS; | 690 | newval |= FUTEX_WAITERS; |
690 | 691 | ||
691 | curval = cmpxchg_futex_value_locked(uaddr, 0, newval); | 692 | if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, 0, newval))) |
692 | |||
693 | if (unlikely(curval == -EFAULT)) | ||
694 | return -EFAULT; | 693 | return -EFAULT; |
695 | 694 | ||
696 | /* | 695 | /* |
@@ -728,9 +727,7 @@ retry: | |||
728 | lock_taken = 1; | 727 | lock_taken = 1; |
729 | } | 728 | } |
730 | 729 | ||
731 | curval = cmpxchg_futex_value_locked(uaddr, uval, newval); | 730 | if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))) |
732 | |||
733 | if (unlikely(curval == -EFAULT)) | ||
734 | return -EFAULT; | 731 | return -EFAULT; |
735 | if (unlikely(curval != uval)) | 732 | if (unlikely(curval != uval)) |
736 | goto retry; | 733 | goto retry; |
@@ -843,9 +840,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) | |||
843 | 840 | ||
844 | newval = FUTEX_WAITERS | task_pid_vnr(new_owner); | 841 | newval = FUTEX_WAITERS | task_pid_vnr(new_owner); |
845 | 842 | ||
846 | curval = cmpxchg_futex_value_locked(uaddr, uval, newval); | 843 | if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) |
847 | |||
848 | if (curval == -EFAULT) | ||
849 | ret = -EFAULT; | 844 | ret = -EFAULT; |
850 | else if (curval != uval) | 845 | else if (curval != uval) |
851 | ret = -EINVAL; | 846 | ret = -EINVAL; |
@@ -880,10 +875,8 @@ static int unlock_futex_pi(u32 __user *uaddr, u32 uval) | |||
880 | * There is no waiter, so we unlock the futex. The owner died | 875 | * There is no waiter, so we unlock the futex. The owner died |
881 | * bit has not to be preserved here. We are the owner: | 876 | * bit has not to be preserved here. We are the owner: |
882 | */ | 877 | */ |
883 | oldval = cmpxchg_futex_value_locked(uaddr, uval, 0); | 878 | if (cmpxchg_futex_value_locked(&oldval, uaddr, uval, 0)) |
884 | 879 | return -EFAULT; | |
885 | if (oldval == -EFAULT) | ||
886 | return oldval; | ||
887 | if (oldval != uval) | 880 | if (oldval != uval) |
888 | return -EAGAIN; | 881 | return -EAGAIN; |
889 | 882 | ||
@@ -1578,9 +1571,7 @@ retry: | |||
1578 | while (1) { | 1571 | while (1) { |
1579 | newval = (uval & FUTEX_OWNER_DIED) | newtid; | 1572 | newval = (uval & FUTEX_OWNER_DIED) | newtid; |
1580 | 1573 | ||
1581 | curval = cmpxchg_futex_value_locked(uaddr, uval, newval); | 1574 | if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) |
1582 | |||
1583 | if (curval == -EFAULT) | ||
1584 | goto handle_fault; | 1575 | goto handle_fault; |
1585 | if (curval == uval) | 1576 | if (curval == uval) |
1586 | break; | 1577 | break; |
@@ -2073,11 +2064,8 @@ retry: | |||
2073 | * again. If it succeeds then we can return without waking | 2064 | * again. If it succeeds then we can return without waking |
2074 | * anyone else up: | 2065 | * anyone else up: |
2075 | */ | 2066 | */ |
2076 | if (!(uval & FUTEX_OWNER_DIED)) | 2067 | if (!(uval & FUTEX_OWNER_DIED) && |
2077 | uval = cmpxchg_futex_value_locked(uaddr, vpid, 0); | 2068 | cmpxchg_futex_value_locked(&uval, uaddr, vpid, 0)) |
2078 | |||
2079 | |||
2080 | if (unlikely(uval == -EFAULT)) | ||
2081 | goto pi_faulted; | 2069 | goto pi_faulted; |
2082 | /* | 2070 | /* |
2083 | * Rare case: we managed to release the lock atomically, | 2071 | * Rare case: we managed to release the lock atomically, |
@@ -2464,9 +2452,7 @@ retry: | |||
2464 | * userspace. | 2452 | * userspace. |
2465 | */ | 2453 | */ |
2466 | mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; | 2454 | mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; |
2467 | nval = futex_atomic_cmpxchg_inatomic(uaddr, uval, mval); | 2455 | if (futex_atomic_cmpxchg_inatomic(&nval, uaddr, uval, mval)) |
2468 | |||
2469 | if (nval == -EFAULT) | ||
2470 | return -1; | 2456 | return -1; |
2471 | 2457 | ||
2472 | if (nval != uval) | 2458 | if (nval != uval) |
@@ -2679,8 +2665,7 @@ static int __init futex_init(void) | |||
2679 | * implementation, the non-functional ones will return | 2665 | * implementation, the non-functional ones will return |
2680 | * -ENOSYS. | 2666 | * -ENOSYS. |
2681 | */ | 2667 | */ |
2682 | curval = cmpxchg_futex_value_locked(NULL, 0, 0); | 2668 | if (cmpxchg_futex_value_locked(&curval, NULL, 0, 0) == -EFAULT) |
2683 | if (curval == -EFAULT) | ||
2684 | futex_cmpxchg_enabled = 1; | 2669 | futex_cmpxchg_enabled = 1; |
2685 | 2670 | ||
2686 | for (i = 0; i < ARRAY_SIZE(futex_queues); i++) { | 2671 | for (i = 0; i < ARRAY_SIZE(futex_queues); i++) { |