diff --git a/debian/changelog b/debian/changelog index b35ed9192..b370cd6ea 100644 --- a/debian/changelog +++ b/debian/changelog @@ -87,6 +87,13 @@ linux (3.14.5-1) UNRELEASED; urgency=medium * [x86] ACPICA: Tables: Fix invalid pointer accesses in acpi_tb_parse_root_table(). (Closes: #748574) * net: Revert lockdep changes in 3.14.5 to avoid an ABI change + * futex: Add another early deadlock detection check + * futex: Prevent attaching to kernel threads + * futex: Forbid uaddr == uaddr2 in futex_requeue(..., requeue_pi=1) + (CVE-2014-3153) + * futex: Validate atomic acquisition in futex_lock_pi_atomic() + * futex: Always cleanup owner tid in unlock_pi + * futex: Make lookup_pi_state more robust [ Ian Campbell ] * [arm64] Initial kernel configuration and packaging (Closes: #745349). diff --git a/debian/patches/bugfix/all/futex-Add-another-early-deadlock-detection-check.patch b/debian/patches/bugfix/all/futex-Add-another-early-deadlock-detection-check.patch new file mode 100644 index 000000000..2f5e14dc7 --- /dev/null +++ b/debian/patches/bugfix/all/futex-Add-another-early-deadlock-detection-check.patch @@ -0,0 +1,158 @@ +From: Thomas Gleixner +Date: Mon, 12 May 2014 20:45:34 +0000 +Subject: futex: Add another early deadlock detection check + +commit 866293ee54227584ffcb4a42f69c1f365974ba7f upstream. + +Dave Jones trinity syscall fuzzer exposed an issue in the deadlock +detection code of rtmutex: + http://lkml.kernel.org/r/20140429151655.GA14277@redhat.com + +That underlying issue has been fixed with a patch to the rtmutex code, +but the futex code must not call into rtmutex in that case because + - it can detect that issue early + - it avoids a different and more complex fixup for backing out + +If the user space variable got manipulated to 0x80000000 which means +no lock holder, but the waiters bit set and an active pi_state in the +kernel is found we can figure out the recursive locking issue by +looking at the pi_state owner. If that is the current task, then we +can safely return -EDEADLK. + +The check should have been added in commit 59fa62451 (futex: Handle +futex_pi OWNER_DIED take over correctly) already, but I did not see +the above issue caused by user space manipulation back then. + +Signed-off-by: Thomas Gleixner +Cc: Dave Jones +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Darren Hart +Cc: Davidlohr Bueso +Cc: Steven Rostedt +Cc: Clark Williams +Cc: Paul McKenney +Cc: Lai Jiangshan +Cc: Roland McGrath +Cc: Carlos ODonell +Cc: Jakub Jelinek +Cc: Michael Kerrisk +Cc: Sebastian Andrzej Siewior +Link: http://lkml.kernel.org/r/20140512201701.097349971@linutronix.de +Signed-off-by: Thomas Gleixner +--- + kernel/futex.c | 47 ++++++++++++++++++++++++++++++++++------------- + 1 file changed, 34 insertions(+), 13 deletions(-) + +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -731,7 +731,8 @@ void exit_pi_state_list(struct task_stru + + static int + lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, +- union futex_key *key, struct futex_pi_state **ps) ++ union futex_key *key, struct futex_pi_state **ps, ++ struct task_struct *task) + { + struct futex_pi_state *pi_state = NULL; + struct futex_q *this, *next; +@@ -772,6 +773,16 @@ lookup_pi_state(u32 uval, struct futex_h + 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. ++ */ ++ if (task && pi_state->owner == task) ++ return -EDEADLK; ++ + atomic_inc(&pi_state->refcount); + *ps = pi_state; + +@@ -921,7 +932,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); ++ ret = lookup_pi_state(uval, hb, key, ps, task); + + if (unlikely(ret)) { + switch (ret) { +@@ -1333,7 +1344,7 @@ void requeue_pi_wake_futex(struct futex_ + * + * Return: + * 0 - failed to acquire the lock atomically; +- * 1 - acquired the lock; ++ * >0 - acquired the lock, return value is vpid of the top_waiter + * <0 - error + */ + static int futex_proxy_trylock_atomic(u32 __user *pifutex, +@@ -1344,7 +1355,7 @@ static int futex_proxy_trylock_atomic(u3 + { + struct futex_q *top_waiter = NULL; + u32 curval; +- int ret; ++ int ret, vpid; + + if (get_futex_value_locked(&curval, pifutex)) + return -EFAULT; +@@ -1372,11 +1383,13 @@ static int futex_proxy_trylock_atomic(u3 + * the contended case or if set_waiters is 1. The pi_state is returned + * in ps in contended cases. + */ ++ vpid = task_pid_vnr(top_waiter->task); + ret = futex_lock_pi_atomic(pifutex, hb2, key2, ps, top_waiter->task, + set_waiters); +- if (ret == 1) ++ if (ret == 1) { + requeue_pi_wake_futex(top_waiter, key2, hb2); +- ++ return vpid; ++ } + return ret; + } + +@@ -1407,7 +1420,6 @@ static int futex_requeue(u32 __user *uad + struct futex_pi_state *pi_state = NULL; + struct futex_hash_bucket *hb1, *hb2; + struct futex_q *this, *next; +- u32 curval2; + + if (requeue_pi) { + /* +@@ -1495,16 +1507,25 @@ retry_private: + * At this point the top_waiter has either taken uaddr2 or is + * waiting on it. If the former, then the pi_state will not + * exist yet, look it up one more time to ensure we have a +- * reference to it. ++ * reference to it. If the lock was taken, ret contains the ++ * vpid of the top waiter task. + */ +- if (ret == 1) { ++ if (ret > 0) { + WARN_ON(pi_state); + drop_count++; + task_count++; +- ret = get_futex_value_locked(&curval2, uaddr2); +- if (!ret) +- ret = lookup_pi_state(curval2, hb2, &key2, +- &pi_state); ++ /* ++ * If we acquired the lock, then the user ++ * space value of uaddr2 should be vpid. It ++ * cannot be changed by the top waiter as it ++ * is blocked on hb2 lock if it tries to do ++ * so. If something fiddled with it behind our ++ * back the pi state lookup might unearth ++ * it. So we rather use the known value than ++ * rereading and handing potential crap to ++ * lookup_pi_state. ++ */ ++ ret = lookup_pi_state(ret, hb2, &key2, &pi_state, NULL); + } + + switch (ret) { diff --git a/debian/patches/bugfix/all/futex-Always-cleanup-owner-tid-in-unlock_pi.patch b/debian/patches/bugfix/all/futex-Always-cleanup-owner-tid-in-unlock_pi.patch new file mode 100644 index 000000000..c4b2bb25f --- /dev/null +++ b/debian/patches/bugfix/all/futex-Always-cleanup-owner-tid-in-unlock_pi.patch @@ -0,0 +1,131 @@ +Return-Path: +Received: from Galois.linutronix.de (Galois.linutronix.de + [IPv6:2001:470:1f0b:db:abcd:42:0:1]) by vinyl.outflux.net + (8.14.4/8.14.4/Debian-4.1ubuntu1) with ESMTP id s53CRBS5010805 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for + ; Tue, 3 Jun 2014 05:27:17 -0700 +Received: from localhost ([127.0.0.1] helo=[127.0.1.1]) by + Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from + ) id 1Wrno4-0002Sb-9g; Tue, 03 Jun 2014 14:27:08 +0200 +Message-Id: <20140603121944.949737592@linutronix.de> +User-Agent: quilt/0.63-1 +Date: Tue, 03 Jun 2014 12:27:07 -0000 +From: Thomas Gleixner +To: Linus Torvalds +Cc: Darren Hart , Kees Cook , + "security@kernel.org" , linux-distros@vs.openwall.org, + Sebastian Krahmer , Ingo Molnar , Kees + Cook , Will Drewry +Subject: [patch 3/4] futex: Always cleanup owner tid in unlock_pi +References: <20140603113303.799564413@linutronix.de> +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-15 +Content-Disposition: inline; filename=futex-cleanup-owner-tid-on-unlock.patch +X-Linutronix-Spam-Score: -1.0 +X-Linutronix-Spam-Level: - +X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, + ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 +Received-SPF: none (linutronix.de: No applicable sender policy available) + receiver=smtp.outflux.net; identity=mailfrom; + envelope-from="tglx@linutronix.de"; helo=Galois.linutronix.de; + client-ip="2001:470:1f0b:db:abcd:42:0:1" +Envelope-To: kees@outflux.net +X-MIMEDefang-Filter: outflux$Revision: 1.316 $ +X-HELO: Galois.linutronix.de +X-Spam-Status: No, hits=-0.651 required=5 tests=RP_MATCHES_RCVD +X-Spam-Checker-Version: SpamAssassin 3.4.0-outflux_revision__1.66__ +X-Scanned-By: MIMEDefang 2.73 +Content-Length: 2854 +Lines: 93 + +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 +Cc: Kees Cook +Cc: Will Drewry +Cc: Darren Hart +Cc: stable@vger.kernel.org +--- + kernel/futex.c | 44 ++++++++++++++++++++------------------------ + 1 file changed, 20 insertions(+), 24 deletions(-) + +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -1038,6 +1038,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; +@@ -1061,23 +1062,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); +@@ -2337,9 +2334,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; + /* +@@ -2369,11 +2367,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/debian/patches/bugfix/all/futex-Make-lookup_pi_state-more-robust.patch b/debian/patches/bugfix/all/futex-Make-lookup_pi_state-more-robust.patch new file mode 100644 index 000000000..db0fd4ee9 --- /dev/null +++ b/debian/patches/bugfix/all/futex-Make-lookup_pi_state-more-robust.patch @@ -0,0 +1,309 @@ +Return-Path: +Received: from Galois.linutronix.de (Galois.linutronix.de + [IPv6:2001:470:1f0b:db:abcd:42:0:1]) by vinyl.outflux.net + (8.14.4/8.14.4/Debian-4.1ubuntu1) with ESMTP id s53CRPJj010831 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for + ; Tue, 3 Jun 2014 05:27:31 -0700 +Received: from localhost ([127.0.0.1] helo=[127.0.1.1]) by + Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from + ) id 1Wrno5-0002Se-1m; Tue, 03 Jun 2014 14:27:09 +0200 +Message-Id: <20140603121945.039282525@linutronix.de> +User-Agent: quilt/0.63-1 +Date: Tue, 03 Jun 2014 12:27:08 -0000 +From: Thomas Gleixner +To: Linus Torvalds +Cc: Darren Hart , Kees Cook , + "security@kernel.org" , linux-distros@vs.openwall.org, + Sebastian Krahmer , Ingo Molnar , Kees + Cook , Will Drewry +Subject: [patch 4/4] futex: Make lookup_pi_state more robust +References: <20140603113303.799564413@linutronix.de> +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-15 +Content-Disposition: inline; filename=futex-make-lookup-pi-state-more-robust.patch +X-Linutronix-Spam-Score: -1.0 +X-Linutronix-Spam-Level: - +X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, + ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 +Received-SPF: none (linutronix.de: No applicable sender policy available) + receiver=smtp.outflux.net; identity=mailfrom; + envelope-from="tglx@linutronix.de"; helo=Galois.linutronix.de; + client-ip="2001:470:1f0b:db:abcd:42:0:1" +Envelope-To: kees@outflux.net +X-MIMEDefang-Filter: outflux$Revision: 1.316 $ +X-HELO: Galois.linutronix.de +X-Spam-Status: No, hits=-0.651 required=5 tests=RP_MATCHES_RCVD +X-Spam-Checker-Version: SpamAssassin 3.4.0-outflux_revision__1.66__ +X-Scanned-By: MIMEDefang 2.73 +Status: RO +Content-Length: 8955 +Lines: 270 + +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 +Cc: Kees Cook +Cc: Will Drewry +Cc: Darren Hart +Cc: stable@vger.kernel.org +--- + kernel/futex.c | 134 +++++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 106 insertions(+), 28 deletions(-) + +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -729,10 +729,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; +@@ -742,12 +790,13 @@ lookup_pi_state(u32 uval, struct futex_h + plist_for_each_entry_safe(this, next, &hb->chain, 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; +@@ -755,44 +804,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; +@@ -825,6 +900,9 @@ lookup_pi_state(u32 uval, struct futex_h + return ret; + } + ++ /* ++ * No existing pi state. First waiter. [2] ++ */ + pi_state = alloc_pi_state(); + + /* +@@ -945,7 +1023,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) { +@@ -1551,7 +1629,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/debian/patches/bugfix/all/futex-Prevent-attaching-to-kernel-threads.patch b/debian/patches/bugfix/all/futex-Prevent-attaching-to-kernel-threads.patch new file mode 100644 index 000000000..8be1947ef --- /dev/null +++ b/debian/patches/bugfix/all/futex-Prevent-attaching-to-kernel-threads.patch @@ -0,0 +1,50 @@ +From: Thomas Gleixner +Date: Mon, 12 May 2014 20:45:35 +0000 +Subject: futex: Prevent attaching to kernel threads +Origin: https://git.kernel.org/linus/f0d71b3dcb8332f7971b5f2363632573e6d9486a + +We happily allow userspace to declare a random kernel thread to be the +owner of a user space PI futex. + +Found while analysing the fallout of Dave Jones syscall fuzzer. + +We also should validate the thread group for private futexes and find +some fast way to validate whether the "alleged" owner has RW access on +the file which backs the SHM, but that's a separate issue. + +Signed-off-by: Thomas Gleixner +Cc: Dave Jones +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Darren Hart +Cc: Davidlohr Bueso +Cc: Steven Rostedt +Cc: Clark Williams +Cc: Paul McKenney +Cc: Lai Jiangshan +Cc: Roland McGrath +Cc: Carlos ODonell +Cc: Jakub Jelinek +Cc: Michael Kerrisk +Cc: Sebastian Andrzej Siewior +Link: http://lkml.kernel.org/r/20140512201701.194824402@linutronix.de +Signed-off-by: Thomas Gleixner +Cc: stable@vger.kernel.org +--- + kernel/futex.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -800,6 +800,11 @@ lookup_pi_state(u32 uval, struct futex_h + if (!p) + return -ESRCH; + ++ if (!p->mm) { ++ put_task_struct(p); ++ return -EPERM; ++ } ++ + /* + * We need to look at the task state flags to figure out, + * whether the task is exiting. To protect against the do_exit diff --git a/debian/patches/bugfix/all/futex-Validate-atomic-acquisition-in-futex_lock_pi_atomic.patch b/debian/patches/bugfix/all/futex-Validate-atomic-acquisition-in-futex_lock_pi_atomic.patch new file mode 100644 index 000000000..6bd7d0e8b --- /dev/null +++ b/debian/patches/bugfix/all/futex-Validate-atomic-acquisition-in-futex_lock_pi_atomic.patch @@ -0,0 +1,86 @@ +Return-Path: +Received: from Galois.linutronix.de (Galois.linutronix.de + [IPv6:2001:470:1f0b:db:abcd:42:0:1]) by vinyl.outflux.net + (8.14.4/8.14.4/Debian-4.1ubuntu1) with ESMTP id s53CRBqO010803 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for + ; Tue, 3 Jun 2014 05:27:17 -0700 +Received: from localhost ([127.0.0.1] helo=[127.0.1.1]) by + Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from + ) id 1Wrno3-0002SY-Hl; Tue, 03 Jun 2014 14:27:07 +0200 +Message-Id: <20140603121944.859726103@linutronix.de> +User-Agent: quilt/0.63-1 +Date: Tue, 03 Jun 2014 12:27:06 -0000 +From: Thomas Gleixner +To: Linus Torvalds +Cc: Darren Hart , Kees Cook , + "security@kernel.org" , linux-distros@vs.openwall.org, + Sebastian Krahmer , Ingo Molnar , Kees + Cook , Will Drewry +Subject: [patch 2/4] futex: Validate atomic acquisition in + futex_lock_pi_atomic() +References: <20140603113303.799564413@linutronix.de> +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-15 +Content-Disposition: inline; filename=futex-validate-atomic-acquisiton.patch +X-Linutronix-Spam-Score: -1.0 +X-Linutronix-Spam-Level: - +X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, + ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 +Received-SPF: none (linutronix.de: No applicable sender policy available) + receiver=smtp.outflux.net; identity=mailfrom; + envelope-from="tglx@linutronix.de"; helo=Galois.linutronix.de; + client-ip="2001:470:1f0b:db:abcd:42:0:1" +Envelope-To: kees@outflux.net +X-MIMEDefang-Filter: outflux$Revision: 1.316 $ +X-HELO: Galois.linutronix.de +X-Spam-Status: No, hits=-0.651 required=5 tests=RP_MATCHES_RCVD +X-Spam-Checker-Version: SpamAssassin 3.4.0-outflux_revision__1.66__ +X-Scanned-By: MIMEDefang 2.73 +Content-Length: 1615 +Lines: 47 + +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 +Cc: Kees Cook +Cc: Will Drewry +Cc: stable@vger.kernel.org +Signed-off-by: Thomas Gleixner +--- + kernel/futex.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -896,10 +896,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; + diff --git a/debian/patches/bugfix/all/futex-prevent-requeue-pi-on-same-futex.patch b/debian/patches/bugfix/all/futex-prevent-requeue-pi-on-same-futex.patch new file mode 100644 index 000000000..370f384de --- /dev/null +++ b/debian/patches/bugfix/all/futex-prevent-requeue-pi-on-same-futex.patch @@ -0,0 +1,113 @@ +Return-Path: +Received: from Galois.linutronix.de (Galois.linutronix.de + [IPv6:2001:470:1f0b:db:abcd:42:0:1]) by vinyl.outflux.net + (8.14.4/8.14.4/Debian-4.1ubuntu1) with ESMTP id s53CRBLI010804 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO) for + ; Tue, 3 Jun 2014 05:27:17 -0700 +Received: from localhost ([127.0.0.1] helo=[127.0.1.1]) by + Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from + ) id 1Wrno2-0002SV-Po; Tue, 03 Jun 2014 14:27:06 +0200 +Message-Id: <20140603121944.770732571@linutronix.de> +User-Agent: quilt/0.63-1 +Date: Tue, 03 Jun 2014 12:27:06 -0000 +From: Thomas Gleixner +To: Linus Torvalds +Cc: Darren Hart , Kees Cook , + "security@kernel.org" , linux-distros@vs.openwall.org, + Sebastian Krahmer , Ingo Molnar , Will + Drewry , Kees Cook +Subject: [patch 1/4] futex-prevent-requeue-pi-on-same-futex.patch futex: + Forbid uaddr == uaddr2 in futex_requeue(..., requeue_pi=1) +References: <20140603113303.799564413@linutronix.de> +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-15 +Content-Disposition: inline; filename=futex-prevent-requeue-pi-on-same-futex.patch +X-Linutronix-Spam-Score: -1.0 +X-Linutronix-Spam-Level: - +X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, + ALL_TRUSTED=-1,SHORTCIRCUIT=-0.0001 +Received-SPF: none (linutronix.de: No applicable sender policy available) + receiver=smtp.outflux.net; identity=mailfrom; + envelope-from="tglx@linutronix.de"; helo=Galois.linutronix.de; + client-ip="2001:470:1f0b:db:abcd:42:0:1" +Envelope-To: kees@outflux.net +X-MIMEDefang-Filter: outflux$Revision: 1.316 $ +X-HELO: Galois.linutronix.de +X-Spam-Status: No, hits=-0.651 required=5 tests=RP_MATCHES_RCVD +X-Spam-Checker-Version: SpamAssassin 3.4.0-outflux_revision__1.66__ +X-Scanned-By: MIMEDefang 2.73 +Status: RO +Content-Length: 2114 +Lines: 73 + +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() into line with +futex_wait_requeue_pi() which performs the same check as per commit +6f7b0a2a5 (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 +Signed-off-by: Kees Cook +Cc: stable@vger.kernel.org +Signed-off-by: Thomas Gleixner +--- + kernel/futex.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -1428,6 +1428,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. + */ +@@ -1465,6 +1472,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); + +@@ -2511,6 +2527,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/debian/patches/series b/debian/patches/series index abf2e67ce..d2f8fe8b5 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -89,3 +89,9 @@ debian/sockdiag-avoid-abi-change-in-3.14.5.patch debian/target-avoid-abi-change-in-3.14.5.patch debian/netfilter-avoid-abi-change-in-3.14.5.patch bugfix/mips/MIPS-Fix-branch-emulation-of-branch-likely-instructi.patch +bugfix/all/futex-Add-another-early-deadlock-detection-check.patch +bugfix/all/futex-Prevent-attaching-to-kernel-threads.patch +bugfix/all/futex-prevent-requeue-pi-on-same-futex.patch +bugfix/all/futex-Validate-atomic-acquisition-in-futex_lock_pi_atomic.patch +bugfix/all/futex-Always-cleanup-owner-tid-in-unlock_pi.patch +bugfix/all/futex-Make-lookup_pi_state-more-robust.patch