SUNRPC: avoid race between mod_timer() and del_timer_sync()
authorNeilBrown <neilb@suse.de>
Tue, 8 Mar 2022 02:42:17 +0000 (13:42 +1100)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 23 Mar 2022 18:50:04 +0000 (14:50 -0400)
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>
net/sunrpc/xprt.c

index 744c6c1d536f07c979e886f5a5af0d891d3bd429..880bfe8dc7f6dcf6ef213d9b7656b4e488aab9e0 100644 (file)
@@ -2104,7 +2104,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