summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2022-03-08 05:42:17 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-04-08 15:39:50 +0300
commitb7b430104a14a05c74297e794a0fe237f822ebb1 (patch)
treee140e8a55ef8af7ed2cfe7ddfd467f4244d276ba /net
parentf51ab2f60a44c42b29010b17f98d1f902de40a5c (diff)
downloadlinux-b7b430104a14a05c74297e794a0fe237f822ebb1.tar.xz
SUNRPC: avoid race between mod_timer() and del_timer_sync()
commit 3848e96edf4788f772d83990022fa7023a233d83 upstream. xprt_destory() claims XPRT_LOCKED and then calls del_timer_sync(). Both xprt_unlock_connect() and xprt_release() call ->release_xprt() which drops XPRT_LOCKED and *then* xprt_schedule_autodisconnect() which calls mod_timer(). This may result in mod_timer() being called *after* del_timer_sync(). When this happens, the timer may fire long after the xprt has been freed, and run_timer_softirq() will probably crash. The pairing of ->release_xprt() and xprt_schedule_autodisconnect() is always called under ->transport_lock. So if we take ->transport_lock to call del_timer_sync(), we can be sure that mod_timer() will run first (if it runs at all). Cc: stable@vger.kernel.org Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r--net/sunrpc/xprt.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 04aaca4b8bf9..46304e647c49 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -2037,7 +2037,14 @@ static void xprt_destroy(struct rpc_xprt *xprt)
*/
wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_UNINTERRUPTIBLE);
+ /*
+ * xprt_schedule_autodisconnect() can run after XPRT_LOCKED
+ * is cleared. We use ->transport_lock to ensure the mod_timer()
+ * can only run *before* del_time_sync(), never after.
+ */
+ spin_lock(&xprt->transport_lock);
del_timer_sync(&xprt->timer);
+ spin_unlock(&xprt->transport_lock);
/*
* Destroy sockets etc from the system workqueue so they can