[SRU groovy 4/9] futex: Ensure the correct return value from futex_lock_pi()
Thadeu Lima de Souza Cascardo
cascardo at canonical.com
Wed Mar 10 11:23:01 UTC 2021
On Tue, Mar 09, 2021 at 02:03:27PM -0300, Thadeu Lima de Souza Cascardo wrote:
> From: Thomas Gleixner <tglx at linutronix.de>
>
> In case that futex_lock_pi() was aborted by a signal or a timeout and the
> task returned without acquiring the rtmutex, but is the designated owner of
> the futex due to a concurrent futex_unlock_pi() fixup_owner() is invoked to
> establish consistent state. In that case it invokes fixup_pi_state_owner()
> which in turn tries to acquire the rtmutex again. If that succeeds then it
> does not propagate this success to fixup_owner() and futex_lock_pi()
> returns -EINTR or -ETIMEOUT despite having the futex locked.
>
> Return success from fixup_pi_state_owner() in all cases where the current
> task owns the rtmutex and therefore the futex and propagate it correctly
> through fixup_owner(). Fixup the other callsite which does not expect a
> positive return value.
>
> Fixes: c1e2f0eaf015 ("futex: Avoid violating the 10th rule of futex")
> Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
> Acked-by: Peter Zijlstra (Intel) <peterz at infradead.org>
> Cc: stable at vger.kernel.org
> (cherry picked from commit 12bb3f7f1b03d5913b3f9d4236a488aa7774dfe9)
[cascardo: reverse applying with 04b79c55201f0 for build bisectability]
> CVE-2021-3347
> Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo at canonical.com>
> ---
> kernel/futex.c | 31 ++++++++++++++++---------------
> 1 file changed, 16 insertions(+), 15 deletions(-)
>
> diff --git a/kernel/futex.c b/kernel/futex.c
> index 22dd904016b1..e8e1dfbcc41a 100644
> --- a/kernel/futex.c
> +++ b/kernel/futex.c
> @@ -2392,8 +2392,8 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
> }
>
> if (__rt_mutex_futex_trylock(&pi_state->pi_mutex)) {
> - /* We got the lock after all, nothing to fix. */
> - ret = 0;
> + /* We got the lock. pi_state is correct. Tell caller. */
> + ret = 1;
> goto out_unlock;
> }
>
> @@ -2421,7 +2421,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
> * We raced against a concurrent self; things are
> * already fixed up. Nothing to do.
> */
> - ret = 0;
> + ret = 1;
> goto out_unlock;
> }
> newowner = argowner;
> @@ -2467,7 +2467,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
> raw_spin_unlock(&newowner->pi_lock);
> raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
>
> - return 0;
> + return argowner == current;
>
> /*
> * In order to reschedule or handle a page fault, we need to drop the
> @@ -2509,7 +2509,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
> * Check if someone else fixed it for us:
> */
> if (pi_state->owner != oldowner) {
> - ret = 0;
> + ret = argowner == current;
> goto out_unlock;
> }
>
> @@ -2542,8 +2542,6 @@ static long futex_wait_restart(struct restart_block *restart);
> */
> static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
> {
> - int ret = 0;
> -
> if (locked) {
> /*
> * Got the lock. We might not be the anticipated owner if we
> @@ -2554,8 +2552,8 @@ static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
> * stable state, anything else needs more attention.
> */
> if (q->pi_state->owner != current)
> - ret = fixup_pi_state_owner(uaddr, q, current);
> - return ret ? ret : locked;
> + return fixup_pi_state_owner(uaddr, q, current);
> + return 1;
> }
>
> /*
> @@ -2566,10 +2564,8 @@ static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
> * Another speculative read; pi_state->owner == current is unstable
> * but needs our attention.
> */
> - if (q->pi_state->owner == current) {
> - ret = fixup_pi_state_owner(uaddr, q, NULL);
> - return ret;
> - }
> + if (q->pi_state->owner == current)
> + return fixup_pi_state_owner(uaddr, q, NULL);
>
> /*
> * Paranoia check. If we did not take the lock, then we should not be
> @@ -2578,7 +2574,7 @@ static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
> if (WARN_ON_ONCE(rt_mutex_owner(&q->pi_state->pi_mutex) == current))
> return fixup_pi_state_owner(uaddr, q, current);
>
> - return ret;
> + return 0;
> }
>
> /**
> @@ -3276,7 +3272,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
> if (q.pi_state && (q.pi_state->owner != current)) {
> spin_lock(q.lock_ptr);
> ret = fixup_pi_state_owner(uaddr2, &q, current);
> - if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
> + if (ret < 0 && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
> pi_state = q.pi_state;
> get_pi_state(pi_state);
> }
> @@ -3286,6 +3282,11 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
> */
> put_pi_state(q.pi_state);
> spin_unlock(q.lock_ptr);
> + /*
> + * Adjust the return value. It's either -EFAULT or
> + * success (1) but the caller expects 0 for success.
> + */
> + ret = ret < 0 ? ret : 0;
> }
> } else {
> struct rt_mutex *pi_mutex;
> --
> 2.27.0
>
>
> --
> kernel-team mailing list
> kernel-team at lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
More information about the kernel-team
mailing list