From 1542b8f0e5e95784381c5bf043af86bd7d7dd2de Mon Sep 17 00:00:00 2001 From: Gratian Crisan Date: Tue, 8 Dec 2020 10:35:40 -0600 Subject: [PATCH 1/2] Revert "futex: Add work-around for inconsistent pi_state" This reverts commit 4fe2aca61b3001a9ef3adf1b6179de239ba16124. Signed-off-by: Gratian Crisan --- kernel/futex.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 8ee0757496a87..5f1cfa2f02b62 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2448,16 +2448,6 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, * Since we just failed the trylock; there must be an owner. */ newowner = rt_mutex_owner(&pi_state->pi_mutex); - WARN_ON(!newowner); - - /* - * pi_state is incorrect, some other task did a lock steal and - * we returned due to timeout or signal without taking the - * rt_mutex. Too late. - */ - if (!newowner) - newowner = rt_mutex_next_owner(&pi_state->pi_mutex); - BUG_ON(!newowner); } else { WARN_ON_ONCE(argowner != current); From 739ed348e481cf3df35c529ff7e213e2f85324b6 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Wed, 4 Nov 2020 16:12:44 +0100 Subject: [PATCH 2/2] futex: Handle transient "ownerless" rtmutex state correctly commit 9f5d1c336a10c0d24e83e40b4c1b9539f7dba627 upstream. Gratian managed to trigger the BUG_ON(!newowner) in fixup_pi_state_owner(). This is one possible chain of events leading to this: Task Prio Operation T1 120 lock(F) T2 120 lock(F) -> blocks (top waiter) T3 50 (RT) lock(F) -> boosts T1 and blocks (new top waiter) XX timeout/ -> wakes T2 signal T1 50 unlock(F) -> wakes T3 (rtmutex->owner == NULL, waiter bit is set) T2 120 cleanup -> try_to_take_mutex() fails because T3 is the top waiter and the lower priority T2 cannot steal the lock. -> fixup_pi_state_owner() sees newowner == NULL -> BUG_ON() The comment states that this is invalid and rt_mutex_real_owner() must return a non NULL owner when the trylock failed, but in case of a queued and woken up waiter rt_mutex_real_owner() == NULL is a valid transient state. The higher priority waiter has simply not yet managed to take over the rtmutex. The BUG_ON() is therefore wrong and this is just another retry condition in fixup_pi_state_owner(). Drop the locks, so that T3 can make progress, and then try the fixup again. Gratian provided a great analysis, traces and a reproducer. The analysis is to the point, but it confused the hell out of that tglx dude who had to page in all the futex horrors again. Condensed version is above. [ tglx: Wrote comment and changelog ] Fixes: c1e2f0eaf015 ("futex: Avoid violating the 10th rule of futex") Reported-by: Gratian Crisan Signed-off-by: Mike Galbraith Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/87a6w6x7bb.fsf@ni.com Link: https://lore.kernel.org/r/87sg9pkvf7.fsf@nanos.tec.linutronix.de Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 891f3eb413ac7d4d5a0c1c033a4700357fe2ae1a) --- kernel/futex.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 5f1cfa2f02b62..4a59883ee756b 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2445,10 +2445,22 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, } /* - * Since we just failed the trylock; there must be an owner. + * The trylock just failed, so either there is an owner or + * there is a higher priority waiter than this one. */ newowner = rt_mutex_owner(&pi_state->pi_mutex); - BUG_ON(!newowner); + /* + * If the higher priority waiter has not yet taken over the + * rtmutex then newowner is NULL. We can't return here with + * that state because it's inconsistent vs. the user space + * state. So drop the locks and try again. It's a valid + * situation and not any different from the other retry + * conditions. + */ + if (unlikely(!newowner)) { + err = -EAGAIN; + goto handle_err; + } } else { WARN_ON_ONCE(argowner != current); if (oldowner == current) {