summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2021-3347/0010-futex-Handle-faults-correctly-for-PI-futexes.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2021-3347/0010-futex-Handle-faults-correctly-for-PI-futexes.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2021-3347/0010-futex-Handle-faults-correctly-for-PI-futexes.patch164
1 files changed, 164 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2021-3347/0010-futex-Handle-faults-correctly-for-PI-futexes.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2021-3347/0010-futex-Handle-faults-correctly-for-PI-futexes.patch
new file mode 100644
index 000000000..321ba84d8
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/CVE-2021-3347/0010-futex-Handle-faults-correctly-for-PI-futexes.patch
@@ -0,0 +1,164 @@
+From ecd62d2e9ab405d9575c3aa8eb44e44e523a0d19 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Mon, 18 Jan 2021 19:01:21 +0100
+Subject: [PATCH] futex: Handle faults correctly for PI futexes
+
+commit 34b1a1ce1458f50ef27c54e28eb9b1947012907a upstream
+
+fixup_pi_state_owner() tries to ensure that the state of the rtmutex,
+pi_state and the user space value related to the PI futex are consistent
+before returning to user space. In case that the user space value update
+faults and the fault cannot be resolved by faulting the page in via
+fault_in_user_writeable() the function returns with -EFAULT and leaves
+the rtmutex and pi_state owner state inconsistent.
+
+A subsequent futex_unlock_pi() operates on the inconsistent pi_state and
+releases the rtmutex despite not owning it which can corrupt the RB tree of
+the rtmutex and cause a subsequent kernel stack use after free.
+
+It was suggested to loop forever in fixup_pi_state_owner() if the fault
+cannot be resolved, but that results in runaway tasks which is especially
+undesired when the problem happens due to a programming error and not due
+to malice.
+
+As the user space value cannot be fixed up, the proper solution is to make
+the rtmutex and the pi_state consistent so both have the same owner. This
+leaves the user space value out of sync. Any subsequent operation on the
+futex will fail because the 10th rule of PI futexes (pi_state owner and
+user space value are consistent) has been violated.
+
+As a consequence this removes the inept attempts of 'fixing' the situation
+in case that the current task owns the rtmutex when returning with an
+unresolvable fault by unlocking the rtmutex which left pi_state::owner and
+rtmutex::owner out of sync in a different and only slightly less dangerous
+way.
+
+Fixes: 1b7558e457ed ("futexes: fix fault handling in futex_lock_pi")
+Reported-by: gzobqq@gmail.com
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/futex.c | 56 ++++++++++++++++++--------------------------------
+ 1 file changed, 20 insertions(+), 36 deletions(-)
+
+diff --git a/kernel/futex.c b/kernel/futex.c
+index 48c37ff4388d..042c2707e913 100644
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -1052,7 +1052,8 @@ static inline void exit_pi_state_list(struct task_struct *curr) { }
+ * FUTEX_OWNER_DIED bit. See [4]
+ *
+ * [10] There is no transient state which leaves owner and user space
+- * TID out of sync.
++ * TID out of sync. Except one error case where the kernel is denied
++ * write access to the user address, see fixup_pi_state_owner().
+ *
+ *
+ * Serialization and lifetime rules:
+@@ -2613,6 +2614,24 @@ static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
+ if (!err)
+ goto retry;
+
++ /*
++ * fault_in_user_writeable() failed so user state is immutable. At
++ * best we can make the kernel state consistent but user state will
++ * be most likely hosed and any subsequent unlock operation will be
++ * rejected due to PI futex rule [10].
++ *
++ * Ensure that the rtmutex owner is also the pi_state owner despite
++ * the user space value claiming something different. There is no
++ * point in unlocking the rtmutex if current is the owner as it
++ * would need to wait until the next waiter has taken the rtmutex
++ * to guarantee consistent state. Keep it simple. Userspace asked
++ * for this wreckaged state.
++ *
++ * The rtmutex has an owner - either current or some other
++ * task. See the EAGAIN loop above.
++ */
++ pi_state_update_owner(pi_state, rt_mutex_owner(&pi_state->pi_mutex));
++
+ return err;
+ }
+
+@@ -2893,7 +2912,6 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags,
+ ktime_t *time, int trylock)
+ {
+ struct hrtimer_sleeper timeout, *to;
+- struct futex_pi_state *pi_state = NULL;
+ struct task_struct *exiting = NULL;
+ struct rt_mutex_waiter rt_waiter;
+ struct futex_hash_bucket *hb;
+@@ -3030,23 +3048,9 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags,
+ if (res)
+ ret = (res < 0) ? res : 0;
+
+- /*
+- * If fixup_owner() faulted and was unable to handle the fault, unlock
+- * it and return the fault to userspace.
+- */
+- if (ret && (rt_mutex_owner(&q.pi_state->pi_mutex) == current)) {
+- pi_state = q.pi_state;
+- get_pi_state(pi_state);
+- }
+-
+ /* Unqueue and drop the lock */
+ unqueue_me_pi(&q);
+
+- if (pi_state) {
+- rt_mutex_futex_unlock(&pi_state->pi_mutex);
+- put_pi_state(pi_state);
+- }
+-
+ goto out_put_key;
+
+ out_unlock_put_key:
+@@ -3312,7 +3316,6 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
+ u32 __user *uaddr2)
+ {
+ struct hrtimer_sleeper timeout, *to;
+- struct futex_pi_state *pi_state = NULL;
+ struct rt_mutex_waiter rt_waiter;
+ struct futex_hash_bucket *hb;
+ union futex_key key2 = FUTEX_KEY_INIT;
+@@ -3390,10 +3393,6 @@ 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 < 0 && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
+- pi_state = q.pi_state;
+- get_pi_state(pi_state);
+- }
+ /*
+ * Drop the reference to the pi state which
+ * the requeue_pi() code acquired for us.
+@@ -3435,25 +3434,10 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
+ if (res)
+ ret = (res < 0) ? res : 0;
+
+- /*
+- * If fixup_pi_state_owner() faulted and was unable to handle
+- * the fault, unlock the rt_mutex and return the fault to
+- * userspace.
+- */
+- if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
+- pi_state = q.pi_state;
+- get_pi_state(pi_state);
+- }
+-
+ /* Unqueue and drop the lock. */
+ unqueue_me_pi(&q);
+ }
+
+- if (pi_state) {
+- rt_mutex_futex_unlock(&pi_state->pi_mutex);
+- put_pi_state(pi_state);
+- }
+-
+ if (ret == -EINTR) {
+ /*
+ * We've already been requeued, but cannot restart by calling
+--
+2.17.1
+