]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Jun 2014 23:11:30 +0000 (16:11 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 5 Jun 2014 23:11:30 +0000 (16:11 -0700)
added patches:
futex-always-cleanup-owner-tid-in-unlock_pi.patch
futex-make-lookup_pi_state-more-robust.patch
futex-prevent-requeue-pi-on-same-futex.patch-futex-forbid-uaddr-uaddr2-in-futex_requeue-...-requeue_pi-1.patch
futex-validate-atomic-acquisition-in-futex_lock_pi_atomic.patch

queue-3.4/futex-always-cleanup-owner-tid-in-unlock_pi.patch [new file with mode: 0644]
queue-3.4/futex-make-lookup_pi_state-more-robust.patch [new file with mode: 0644]
queue-3.4/futex-prevent-requeue-pi-on-same-futex.patch-futex-forbid-uaddr-uaddr2-in-futex_requeue-...-requeue_pi-1.patch [new file with mode: 0644]
queue-3.4/futex-validate-atomic-acquisition-in-futex_lock_pi_atomic.patch [new file with mode: 0644]
queue-3.4/series

diff --git a/queue-3.4/futex-always-cleanup-owner-tid-in-unlock_pi.patch b/queue-3.4/futex-always-cleanup-owner-tid-in-unlock_pi.patch
new file mode 100644 (file)
index 0000000..4b9a36b
--- /dev/null
@@ -0,0 +1,102 @@
+From 13fbca4c6ecd96ec1a1cfa2e4f2ce191fe928a5e Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 3 Jun 2014 12:27:07 +0000
+Subject: futex: Always cleanup owner tid in unlock_pi
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 13fbca4c6ecd96ec1a1cfa2e4f2ce191fe928a5e upstream.
+
+If the owner died bit is set at futex_unlock_pi, we currently do not
+cleanup the user space futex.  So the owner TID of the current owner
+(the unlocker) persists.  That's observable inconsistant state,
+especially when the ownership of the pi state got transferred.
+
+Clean it up unconditionally.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: Kees Cook <keescook@chromium.org>
+Cc: Will Drewry <wad@chromium.org>
+Cc: Darren Hart <dvhart@linux.intel.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/futex.c |   44 ++++++++++++++++++++------------------------
+ 1 file changed, 20 insertions(+), 24 deletions(-)
+
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -899,6 +899,7 @@ static int wake_futex_pi(u32 __user *uad
+       struct task_struct *new_owner;
+       struct futex_pi_state *pi_state = this->pi_state;
+       u32 uninitialized_var(curval), newval;
++      int ret = 0;
+       if (!pi_state)
+               return -EINVAL;
+@@ -922,23 +923,19 @@ static int wake_futex_pi(u32 __user *uad
+               new_owner = this->task;
+       /*
+-       * We pass it to the next owner. (The WAITERS bit is always
+-       * kept enabled while there is PI state around. We must also
+-       * preserve the owner died bit.)
+-       */
+-      if (!(uval & FUTEX_OWNER_DIED)) {
+-              int ret = 0;
+-
+-              newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
+-
+-              if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
+-                      ret = -EFAULT;
+-              else if (curval != uval)
+-                      ret = -EINVAL;
+-              if (ret) {
+-                      raw_spin_unlock(&pi_state->pi_mutex.wait_lock);
+-                      return ret;
+-              }
++       * We pass it to the next owner. The WAITERS bit is always
++       * kept enabled while there is PI state around. We cleanup the
++       * owner died bit, because we are the owner.
++       */
++      newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
++
++      if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
++              ret = -EFAULT;
++      else if (curval != uval)
++              ret = -EINVAL;
++      if (ret) {
++              raw_spin_unlock(&pi_state->pi_mutex.wait_lock);
++              return ret;
+       }
+       raw_spin_lock_irq(&pi_state->owner->pi_lock);
+@@ -2183,9 +2180,10 @@ retry:
+       /*
+        * To avoid races, try to do the TID -> 0 atomic transition
+        * again. If it succeeds then we can return without waking
+-       * anyone else up:
++       * anyone else up. We only try this if neither the waiters nor
++       * the owner died bit are set.
+        */
+-      if (!(uval & FUTEX_OWNER_DIED) &&
++      if (!(uval & ~FUTEX_TID_MASK) &&
+           cmpxchg_futex_value_locked(&uval, uaddr, vpid, 0))
+               goto pi_faulted;
+       /*
+@@ -2217,11 +2215,9 @@ retry:
+       /*
+        * No waiters - kernel unlocks the futex:
+        */
+-      if (!(uval & FUTEX_OWNER_DIED)) {
+-              ret = unlock_futex_pi(uaddr, uval);
+-              if (ret == -EFAULT)
+-                      goto pi_faulted;
+-      }
++      ret = unlock_futex_pi(uaddr, uval);
++      if (ret == -EFAULT)
++              goto pi_faulted;
+ out_unlock:
+       spin_unlock(&hb->lock);
diff --git a/queue-3.4/futex-make-lookup_pi_state-more-robust.patch b/queue-3.4/futex-make-lookup_pi_state-more-robust.patch
new file mode 100644 (file)
index 0000000..ea58e52
--- /dev/null
@@ -0,0 +1,279 @@
+From 54a217887a7b658e2650c3feff22756ab80c7339 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 3 Jun 2014 12:27:08 +0000
+Subject: futex: Make lookup_pi_state more robust
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit 54a217887a7b658e2650c3feff22756ab80c7339 upstream.
+
+The current implementation of lookup_pi_state has ambigous handling of
+the TID value 0 in the user space futex.  We can get into the kernel
+even if the TID value is 0, because either there is a stale waiters bit
+or the owner died bit is set or we are called from the requeue_pi path
+or from user space just for fun.
+
+The current code avoids an explicit sanity check for pid = 0 in case
+that kernel internal state (waiters) are found for the user space
+address.  This can lead to state leakage and worse under some
+circumstances.
+
+Handle the cases explicit:
+
+       Waiter | pi_state | pi->owner | uTID      | uODIED | ?
+
+  [1]  NULL   | ---      | ---       | 0         | 0/1    | Valid
+  [2]  NULL   | ---      | ---       | >0        | 0/1    | Valid
+
+  [3]  Found  | NULL     | --        | Any       | 0/1    | Invalid
+
+  [4]  Found  | Found    | NULL      | 0         | 1      | Valid
+  [5]  Found  | Found    | NULL      | >0        | 1      | Invalid
+
+  [6]  Found  | Found    | task      | 0         | 1      | Valid
+
+  [7]  Found  | Found    | NULL      | Any       | 0      | Invalid
+
+  [8]  Found  | Found    | task      | ==taskTID | 0/1    | Valid
+  [9]  Found  | Found    | task      | 0         | 0      | Invalid
+  [10] Found  | Found    | task      | !=taskTID | 0/1    | Invalid
+
+ [1] Indicates that the kernel can acquire the futex atomically. We
+     came came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit.
+
+ [2] Valid, if TID does not belong to a kernel thread. If no matching
+     thread is found then it indicates that the owner TID has died.
+
+ [3] Invalid. The waiter is queued on a non PI futex
+
+ [4] Valid state after exit_robust_list(), which sets the user space
+     value to FUTEX_WAITERS | FUTEX_OWNER_DIED.
+
+ [5] The user space value got manipulated between exit_robust_list()
+     and exit_pi_state_list()
+
+ [6] Valid state after exit_pi_state_list() which sets the new owner in
+     the pi_state but cannot access the user space value.
+
+ [7] pi_state->owner can only be NULL when the OWNER_DIED bit is set.
+
+ [8] Owner and user space value match
+
+ [9] There is no transient state which sets the user space TID to 0
+     except exit_robust_list(), but this is indicated by the
+     FUTEX_OWNER_DIED bit. See [4]
+
+[10] There is no transient state which leaves owner and user space
+     TID out of sync.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Cc: Kees Cook <keescook@chromium.org>
+Cc: Will Drewry <wad@chromium.org>
+Cc: Darren Hart <dvhart@linux.intel.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/futex.c |  134 +++++++++++++++++++++++++++++++++++++++++++++------------
+ 1 file changed, 106 insertions(+), 28 deletions(-)
+
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -588,10 +588,58 @@ void exit_pi_state_list(struct task_stru
+       raw_spin_unlock_irq(&curr->pi_lock);
+ }
++/*
++ * We need to check the following states:
++ *
++ *      Waiter | pi_state | pi->owner | uTID      | uODIED | ?
++ *
++ * [1]  NULL   | ---      | ---       | 0         | 0/1    | Valid
++ * [2]  NULL   | ---      | ---       | >0        | 0/1    | Valid
++ *
++ * [3]  Found  | NULL     | --        | Any       | 0/1    | Invalid
++ *
++ * [4]  Found  | Found    | NULL      | 0         | 1      | Valid
++ * [5]  Found  | Found    | NULL      | >0        | 1      | Invalid
++ *
++ * [6]  Found  | Found    | task      | 0         | 1      | Valid
++ *
++ * [7]  Found  | Found    | NULL      | Any       | 0      | Invalid
++ *
++ * [8]  Found  | Found    | task      | ==taskTID | 0/1    | Valid
++ * [9]  Found  | Found    | task      | 0         | 0      | Invalid
++ * [10] Found  | Found    | task      | !=taskTID | 0/1    | Invalid
++ *
++ * [1]        Indicates that the kernel can acquire the futex atomically. We
++ *    came came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit.
++ *
++ * [2]        Valid, if TID does not belong to a kernel thread. If no matching
++ *      thread is found then it indicates that the owner TID has died.
++ *
++ * [3]        Invalid. The waiter is queued on a non PI futex
++ *
++ * [4]        Valid state after exit_robust_list(), which sets the user space
++ *    value to FUTEX_WAITERS | FUTEX_OWNER_DIED.
++ *
++ * [5]        The user space value got manipulated between exit_robust_list()
++ *    and exit_pi_state_list()
++ *
++ * [6]        Valid state after exit_pi_state_list() which sets the new owner in
++ *    the pi_state but cannot access the user space value.
++ *
++ * [7]        pi_state->owner can only be NULL when the OWNER_DIED bit is set.
++ *
++ * [8]        Owner and user space value match
++ *
++ * [9]        There is no transient state which sets the user space TID to 0
++ *    except exit_robust_list(), but this is indicated by the
++ *    FUTEX_OWNER_DIED bit. See [4]
++ *
++ * [10] There is no transient state which leaves owner and user space
++ *    TID out of sync.
++ */
+ static int
+ lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
+-              union futex_key *key, struct futex_pi_state **ps,
+-              struct task_struct *task)
++              union futex_key *key, struct futex_pi_state **ps)
+ {
+       struct futex_pi_state *pi_state = NULL;
+       struct futex_q *this, *next;
+@@ -604,12 +652,13 @@ lookup_pi_state(u32 uval, struct futex_h
+       plist_for_each_entry_safe(this, next, head, list) {
+               if (match_futex(&this->key, key)) {
+                       /*
+-                       * Another waiter already exists - bump up
+-                       * the refcount and return its pi_state:
++                       * Sanity check the waiter before increasing
++                       * the refcount and attaching to it.
+                        */
+                       pi_state = this->pi_state;
+                       /*
+-                       * Userspace might have messed up non-PI and PI futexes
++                       * Userspace might have messed up non-PI and
++                       * PI futexes [3]
+                        */
+                       if (unlikely(!pi_state))
+                               return -EINVAL;
+@@ -617,44 +666,70 @@ lookup_pi_state(u32 uval, struct futex_h
+                       WARN_ON(!atomic_read(&pi_state->refcount));
+                       /*
+-                       * When pi_state->owner is NULL then the owner died
+-                       * and another waiter is on the fly. pi_state->owner
+-                       * is fixed up by the task which acquires
+-                       * pi_state->rt_mutex.
+-                       *
+-                       * We do not check for pid == 0 which can happen when
+-                       * the owner died and robust_list_exit() cleared the
+-                       * TID.
++                       * Handle the owner died case:
+                        */
+-                      if (pid && pi_state->owner) {
++                      if (uval & FUTEX_OWNER_DIED) {
+                               /*
+-                               * Bail out if user space manipulated the
+-                               * futex value.
++                               * exit_pi_state_list sets owner to NULL and
++                               * wakes the topmost waiter. The task which
++                               * acquires the pi_state->rt_mutex will fixup
++                               * owner.
+                                */
+-                              if (pid != task_pid_vnr(pi_state->owner))
++                              if (!pi_state->owner) {
++                                      /*
++                                       * No pi state owner, but the user
++                                       * space TID is not 0. Inconsistent
++                                       * state. [5]
++                                       */
++                                      if (pid)
++                                              return -EINVAL;
++                                      /*
++                                       * Take a ref on the state and
++                                       * return. [4]
++                                       */
++                                      goto out_state;
++                              }
++
++                              /*
++                               * If TID is 0, then either the dying owner
++                               * has not yet executed exit_pi_state_list()
++                               * or some waiter acquired the rtmutex in the
++                               * pi state, but did not yet fixup the TID in
++                               * user space.
++                               *
++                               * Take a ref on the state and return. [6]
++                               */
++                              if (!pid)
++                                      goto out_state;
++                      } else {
++                              /*
++                               * If the owner died bit is not set,
++                               * then the pi_state must have an
++                               * owner. [7]
++                               */
++                              if (!pi_state->owner)
+                                       return -EINVAL;
+                       }
+                       /*
+-                       * Protect against a corrupted uval. If uval
+-                       * is 0x80000000 then pid is 0 and the waiter
+-                       * bit is set. So the deadlock check in the
+-                       * calling code has failed and we did not fall
+-                       * into the check above due to !pid.
++                       * Bail out if user space manipulated the
++                       * futex value. If pi state exists then the
++                       * owner TID must be the same as the user
++                       * space TID. [9/10]
+                        */
+-                      if (task && pi_state->owner == task)
+-                              return -EDEADLK;
++                      if (pid != task_pid_vnr(pi_state->owner))
++                              return -EINVAL;
++              out_state:
+                       atomic_inc(&pi_state->refcount);
+                       *ps = pi_state;
+-
+                       return 0;
+               }
+       }
+       /*
+        * We are the first waiter - try to look up the real owner and attach
+-       * the new pi_state to it, but bail out when TID = 0
++       * the new pi_state to it, but bail out when TID = 0 [1]
+        */
+       if (!pid)
+               return -ESRCH;
+@@ -687,6 +762,9 @@ lookup_pi_state(u32 uval, struct futex_h
+               return ret;
+       }
++      /*
++       * No existing pi state. First waiter. [2]
++       */
+       pi_state = alloc_pi_state();
+       /*
+@@ -807,7 +885,7 @@ retry:
+        * We dont have the lock. Look up the PI state (or create it if
+        * we are the first waiter):
+        */
+-      ret = lookup_pi_state(uval, hb, key, ps, task);
++      ret = lookup_pi_state(uval, hb, key, ps);
+       if (unlikely(ret)) {
+               switch (ret) {
+@@ -1410,7 +1488,7 @@ retry_private:
+                        * rereading and handing potential crap to
+                        * lookup_pi_state.
+                        */
+-                      ret = lookup_pi_state(ret, hb2, &key2, &pi_state, NULL);
++                      ret = lookup_pi_state(ret, hb2, &key2, &pi_state);
+               }
+               switch (ret) {
diff --git a/queue-3.4/futex-prevent-requeue-pi-on-same-futex.patch-futex-forbid-uaddr-uaddr2-in-futex_requeue-...-requeue_pi-1.patch b/queue-3.4/futex-prevent-requeue-pi-on-same-futex.patch-futex-forbid-uaddr-uaddr2-in-futex_requeue-...-requeue_pi-1.patch
new file mode 100644 (file)
index 0000000..a4f13ac
--- /dev/null
@@ -0,0 +1,83 @@
+From e9c243a5a6de0be8e584c604d353412584b592f8 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 3 Jun 2014 12:27:06 +0000
+Subject: futex-prevent-requeue-pi-on-same-futex.patch futex: Forbid uaddr == uaddr2 in futex_requeue(..., requeue_pi=1)
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit e9c243a5a6de0be8e584c604d353412584b592f8 upstream.
+
+If uaddr == uaddr2, then we have broken the rule of only requeueing from
+a non-pi futex to a pi futex with this call.  If we attempt this, then
+dangling pointers may be left for rt_waiter resulting in an exploitable
+condition.
+
+This change brings futex_requeue() in line with futex_wait_requeue_pi()
+which performs the same check as per commit 6f7b0a2a5c0f ("futex: Forbid
+uaddr == uaddr2 in futex_wait_requeue_pi()")
+
+[ tglx: Compare the resulting keys as well, as uaddrs might be
+       different depending on the mapping ]
+
+Fixes CVE-2014-3153.
+
+Reported-by: Pinkie Pie
+Signed-off-by: Will Drewry <wad@chromium.org>
+Signed-off-by: Kees Cook <keescook@chromium.org>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Reviewed-by: Darren Hart <dvhart@linux.intel.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/futex.c |   25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -1289,6 +1289,13 @@ static int futex_requeue(u32 __user *uad
+       if (requeue_pi) {
+               /*
++               * Requeue PI only works on two distinct uaddrs. This
++               * check is only valid for private futexes. See below.
++               */
++              if (uaddr1 == uaddr2)
++                      return -EINVAL;
++
++              /*
+                * requeue_pi requires a pi_state, try to allocate it now
+                * without any locks in case it fails.
+                */
+@@ -1326,6 +1333,15 @@ retry:
+       if (unlikely(ret != 0))
+               goto out_put_key1;
++      /*
++       * The check above which compares uaddrs is not sufficient for
++       * shared futexes. We need to compare the keys:
++       */
++      if (requeue_pi && match_futex(&key1, &key2)) {
++              ret = -EINVAL;
++              goto out_put_keys;
++      }
++
+       hb1 = hash_futex(&key1);
+       hb2 = hash_futex(&key2);
+@@ -2357,6 +2373,15 @@ static int futex_wait_requeue_pi(u32 __u
+       if (ret)
+               goto out_key2;
++      /*
++       * The check above which compares uaddrs is not sufficient for
++       * shared futexes. We need to compare the keys:
++       */
++      if (match_futex(&q.key, &key2)) {
++              ret = -EINVAL;
++              goto out_put_keys;
++      }
++
+       /* Queue the futex_q, drop the hb lock, wait for wakeup. */
+       futex_wait_queue_me(hb, &q, to);
diff --git a/queue-3.4/futex-validate-atomic-acquisition-in-futex_lock_pi_atomic.patch b/queue-3.4/futex-validate-atomic-acquisition-in-futex_lock_pi_atomic.patch
new file mode 100644 (file)
index 0000000..c0928eb
--- /dev/null
@@ -0,0 +1,56 @@
+From b3eaa9fc5cd0a4d74b18f6b8dc617aeaf1873270 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Tue, 3 Jun 2014 12:27:06 +0000
+Subject: futex: Validate atomic acquisition in futex_lock_pi_atomic()
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+commit b3eaa9fc5cd0a4d74b18f6b8dc617aeaf1873270 upstream.
+
+We need to protect the atomic acquisition in the kernel against rogue
+user space which sets the user space futex to 0, so the kernel side
+acquisition succeeds while there is existing state in the kernel
+associated to the real owner.
+
+Verify whether the futex has waiters associated with kernel state.  If
+it has, return -EINVAL.  The state is corrupted already, so no point in
+cleaning it up.  Subsequent calls will fail as well.  Not our problem.
+
+[ tglx: Use futex_top_waiter() and explain why we do not need to try
+       restoring the already corrupted user space state. ]
+
+Signed-off-by: Darren Hart <dvhart@linux.intel.com>
+Cc: Kees Cook <keescook@chromium.org>
+Cc: Will Drewry <wad@chromium.org>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/futex.c |   14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/kernel/futex.c
++++ b/kernel/futex.c
+@@ -758,10 +758,18 @@ retry:
+               return -EDEADLK;
+       /*
+-       * Surprise - we got the lock. Just return to userspace:
++       * Surprise - we got the lock, but we do not trust user space at all.
+        */
+-      if (unlikely(!curval))
+-              return 1;
++      if (unlikely(!curval)) {
++              /*
++               * We verify whether there is kernel state for this
++               * futex. If not, we can safely assume, that the 0 ->
++               * TID transition is correct. If state exists, we do
++               * not bother to fixup the user space state as it was
++               * corrupted already.
++               */
++              return futex_top_waiter(hb, key) ? -EINVAL : 1;
++      }
+       uval = curval;
index e29b24833577ca1b27a894c00ac1b2b707e01bb4..c8f677151e70f756374e69595b5a94624b97eab0 100644 (file)
@@ -214,3 +214,7 @@ pci-aspm-don-t-touch-aspm-if-forcibly-disabled.patch
 hid-logitech-don-t-use-stack-based-dj_report-structures.patch
 dj-memory-scribble-in-logi_dj.patch
 ath9k-protect-tid-sched-check.patch
+futex-prevent-requeue-pi-on-same-futex.patch-futex-forbid-uaddr-uaddr2-in-futex_requeue-...-requeue_pi-1.patch
+futex-validate-atomic-acquisition-in-futex_lock_pi_atomic.patch
+futex-always-cleanup-owner-tid-in-unlock_pi.patch
+futex-make-lookup_pi_state-more-robust.patch