Add futex security fixes including CVE-2014-3153

svn path=/dists/sid/linux/; revision=21395
This commit is contained in:
Ben Hutchings 2014-06-05 12:44:33 +00:00
parent 013ec287e6
commit f0f833e1d4
8 changed files with 860 additions and 0 deletions

7
debian/changelog vendored
View File

@ -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).

View File

@ -0,0 +1,158 @@
From: Thomas Gleixner <tglx@linutronix.de>
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 <tglx@linutronix.de>
Cc: Dave Jones <davej@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Darren Hart <darren@dvhart.com>
Cc: Davidlohr Bueso <davidlohr@hp.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Clark Williams <williams@redhat.com>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Roland McGrath <roland@hack.frob.com>
Cc: Carlos ODonell <carlos@redhat.com>
Cc: Jakub Jelinek <jakub@redhat.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: http://lkml.kernel.org/r/20140512201701.097349971@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
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) {

View File

@ -0,0 +1,131 @@
Return-Path: <tglx@linutronix.de>
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
<kees@outflux.net>; 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
<tglx@linutronix.de>) 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 <tglx@linutronix.de>
To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Darren Hart <dvhart@linux.intel.com>, Kees Cook <kees@outflux.net>,
"security@kernel.org" <security@kernel.org>, linux-distros@vs.openwall.org,
Sebastian Krahmer <krahmer@suse.de>, Ingo Molnar <mingo@kernel.org>, Kees
Cook <keescook@chromium.org>, Will Drewry <wad@chromium.org>
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 <tglx@linutronix.de>
Cc: Kees Cook <keescook@chromium.org>
Cc: Will Drewry <wad@chromium.org>
Cc: Darren Hart <dvhart@linux.intel.com>
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);

View File

@ -0,0 +1,309 @@
Return-Path: <tglx@linutronix.de>
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
<kees@outflux.net>; 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
<tglx@linutronix.de>) 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 <tglx@linutronix.de>
To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Darren Hart <dvhart@linux.intel.com>, Kees Cook <kees@outflux.net>,
"security@kernel.org" <security@kernel.org>, linux-distros@vs.openwall.org,
Sebastian Krahmer <krahmer@suse.de>, Ingo Molnar <mingo@kernel.org>, Kees
Cook <keescook@chromium.org>, Will Drewry <wad@chromium.org>
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 <tglx@linutronix.de>
Cc: Kees Cook <keescook@chromium.org>
Cc: Will Drewry <wad@chromium.org>
Cc: Darren Hart <dvhart@linux.intel.com>
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) {

View File

@ -0,0 +1,50 @@
From: Thomas Gleixner <tglx@linutronix.de>
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 <tglx@linutronix.de>
Cc: Dave Jones <davej@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Darren Hart <darren@dvhart.com>
Cc: Davidlohr Bueso <davidlohr@hp.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Clark Williams <williams@redhat.com>
Cc: Paul McKenney <paulmck@linux.vnet.ibm.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Roland McGrath <roland@hack.frob.com>
Cc: Carlos ODonell <carlos@redhat.com>
Cc: Jakub Jelinek <jakub@redhat.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: http://lkml.kernel.org/r/20140512201701.194824402@linutronix.de
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
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

View File

@ -0,0 +1,86 @@
Return-Path: <tglx@linutronix.de>
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
<kees@outflux.net>; 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
<tglx@linutronix.de>) 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 <tglx@linutronix.de>
To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Darren Hart <dvhart@linux.intel.com>, Kees Cook <kees@outflux.net>,
"security@kernel.org" <security@kernel.org>, linux-distros@vs.openwall.org,
Sebastian Krahmer <krahmer@suse.de>, Ingo Molnar <mingo@kernel.org>, Kees
Cook <keescook@chromium.org>, Will Drewry <wad@chromium.org>
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 <dvhart@linux.intel.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Will Drewry <wad@chromium.org>
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
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;

View File

@ -0,0 +1,113 @@
Return-Path: <tglx@linutronix.de>
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
<kees@outflux.net>; 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
<tglx@linutronix.de>) 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 <tglx@linutronix.de>
To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Darren Hart <dvhart@linux.intel.com>, Kees Cook <kees@outflux.net>,
"security@kernel.org" <security@kernel.org>, linux-distros@vs.openwall.org,
Sebastian Krahmer <krahmer@suse.de>, Ingo Molnar <mingo@kernel.org>, Will
Drewry <wad@chromium.org>, Kees Cook <keescook@chromium.org>
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 <wad@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
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);

View File

@ -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