[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20110206232253.294757001@pcw.home.local>
Date: Mon, 07 Feb 2011 00:23:10 +0100
From: Willy Tarreau <w@....eu>
To: linux-kernel@...r.kernel.org, stable@...nel.org,
stable-review@...nel.org
Cc: NeilBrown <neilb@...e.de>, "J. Bruce Fields" <bfields@...hat.com>,
Greg Kroah-Hartman <gregkh@...e.de>, Willy Tarreau <w@....eu>
Subject: [PATCH 18/23] sunrpc: prevent use-after-free on clearing XPT_BUSY
2.6.27.58-stable review patch. If anyone has any objections, please let us know.
------------------
From: NeilBrown <neilb@...e.de>
commit ed2849d3ecfa339435818eeff28f6c3424300cec upstream.
When an xprt is created, it has a refcount of 1, and XPT_BUSY is set.
The refcount is *not* owned by the thread that created the xprt
(as is clear from the fact that creators never put the reference).
Rather, it is owned by the absence of XPT_DEAD. Once XPT_DEAD is set,
(And XPT_BUSY is clear) that initial reference is dropped and the xprt
can be freed.
So when a creator clears XPT_BUSY it is dropping its only reference and
so must not touch the xprt again.
However svc_recv, after calling ->xpo_accept (and so getting an XPT_BUSY
reference on a new xprt), calls svc_xprt_recieved. This clears
XPT_BUSY and then svc_xprt_enqueue - this last without owning a reference.
This is dangerous and has been seen to leave svc_xprt_enqueue working
with an xprt containing garbage.
So we need to hold an extra counted reference over that call to
svc_xprt_received.
For safety, any time we clear XPT_BUSY and then use the xprt again, we
first get a reference, and the put it again afterwards.
Note that svc_close_all does not need this extra protection as there are
no threads running, and the final free can only be called asynchronously
from such a thread.
Signed-off-by: NeilBrown <neilb@...e.de>
Signed-off-by: J. Bruce Fields <bfields@...hat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@...e.de>
Signed-off-by: Willy Tarreau <w@....eu>
---
net/sunrpc/svc_xprt.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
Index: longterm-2.6.27/net/sunrpc/svc_xprt.c
===================================================================
--- longterm-2.6.27.orig/net/sunrpc/svc_xprt.c 2011-01-29 11:19:14.731064813 +0100
+++ longterm-2.6.27/net/sunrpc/svc_xprt.c 2011-01-29 11:27:22.220064064 +0100
@@ -172,6 +172,7 @@
spin_lock(&svc_xprt_class_lock);
list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) {
struct svc_xprt *newxprt;
+ unsigned short newport;
if (strcmp(xprt_name, xcl->xcl_name))
continue;
@@ -192,8 +193,9 @@
spin_lock_bh(&serv->sv_lock);
list_add(&newxprt->xpt_list, &serv->sv_permsocks);
spin_unlock_bh(&serv->sv_lock);
+ newport = svc_xprt_local_port(newxprt);
clear_bit(XPT_BUSY, &newxprt->xpt_flags);
- return svc_xprt_local_port(newxprt);
+ return newport;
}
err:
spin_unlock(&svc_xprt_class_lock);
@@ -386,8 +388,13 @@
{
BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags));
xprt->xpt_pool = NULL;
+ /* As soon as we clear busy, the xprt could be closed and
+ * 'put', so we need a reference to call svc_xprt_enqueue with:
+ */
+ svc_xprt_get(xprt);
clear_bit(XPT_BUSY, &xprt->xpt_flags);
svc_xprt_enqueue(xprt);
+ svc_xprt_put(xprt);
}
EXPORT_SYMBOL_GPL(svc_xprt_received);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists