]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFS: Use nlmclnt_shutdown_rpc_clnt() to safely shut down NLM
authorChuck Lever <chuck.lever@oracle.com>
Wed, 28 Jan 2026 15:19:27 +0000 (10:19 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 30 Mar 2026 01:25:09 +0000 (21:25 -0400)
A race condition exists in shutdown_store() when writing to the sysfs
"shutdown" file concurrently with nlm_shutdown_hosts_net(). Without
synchronization, the following sequence can occur:

  1. shutdown_store() reads server->nlm_host (non-NULL)
  2. nlm_shutdown_hosts_net() acquires nlm_host_mutex, calls
     rpc_shutdown_client(), sets h_rpcclnt to NULL, and potentially
     frees the host via nlm_gc_hosts()
  3. shutdown_store() dereferences the now-stale or freed host

Introduce nlmclnt_shutdown_rpc_clnt(), which acquires nlm_host_mutex
before accessing h_rpcclnt. This synchronizes with
nlm_shutdown_hosts_net() and ensures the rpc_clnt pointer remains
valid during the shutdown operation.

This change also improves API layering: NFS client code no longer
needs to include the internal lockd header to access nlm_host fields.
The new helper resides in bind.h alongside other public lockd
interfaces.

Reported-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/lockd/host.c
fs/nfs/sysfs.c
include/linux/lockd/bind.h

index 1a9582a10a86f4ebbcf85b3d063537f066245c25..015900d2d4c22931ace58b960326386d64a5bbe6 100644 (file)
@@ -306,6 +306,35 @@ void nlmclnt_release_host(struct nlm_host *host)
        }
 }
 
+/* Callback for rpc_cancel_tasks() - matches all tasks for cancellation */
+static bool nlmclnt_match_all(const struct rpc_task *task, const void *data)
+{
+       return true;
+}
+
+/**
+ * nlmclnt_shutdown_rpc_clnt - safely shut down NLM client RPC operations
+ * @host: nlm_host to shut down
+ *
+ * Cancels outstanding RPC tasks and marks the client as shut down.
+ * Synchronizes with nlmclnt_release_host() via nlm_host_mutex to prevent
+ * races between shutdown and host destruction. Safe to call if h_rpcclnt
+ * is NULL or already shut down.
+ */
+void nlmclnt_shutdown_rpc_clnt(struct nlm_host *host)
+{
+       struct rpc_clnt *clnt;
+
+       mutex_lock(&nlm_host_mutex);
+       clnt = host->h_rpcclnt;
+       if (clnt) {
+               clnt->cl_shutdown = 1;
+               rpc_cancel_tasks(clnt, -EIO, nlmclnt_match_all, NULL);
+       }
+       mutex_unlock(&nlm_host_mutex);
+}
+EXPORT_SYMBOL_GPL(nlmclnt_shutdown_rpc_clnt);
+
 /**
  * nlmsvc_lookup_host - Find an NLM host handle matching a remote client
  * @rqstp: incoming NLM request
index 7d8921f524a694ea7eaca1817d4f7a194cc6da40..051da37770d8d5c4d2a241b98fe95bc88e91a654 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/string.h>
 #include <linux/nfs_fs.h>
 #include <linux/rcupdate.h>
-#include <linux/lockd/lockd.h>
+#include <linux/lockd/bind.h>
 
 #include "internal.h"
 #include "nfs4_fs.h"
@@ -285,7 +285,7 @@ shutdown_store(struct kobject *kobj, struct kobj_attribute *attr,
                shutdown_client(server->client_acl);
 
        if (server->nlm_host)
-               shutdown_client(server->nlm_host->h_rpcclnt);
+               nlmclnt_shutdown_rpc_clnt(server->nlm_host);
 out:
        shutdown_nfs_client(server->nfs_client);
        return count;
index 82eca0a13ccc71badab5e3b06523e7f070824c22..39c124dcb19c8f882859184da2cd1aa1b65f37d1 100644 (file)
@@ -57,6 +57,7 @@ struct nlmclnt_initdata {
 extern struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init);
 extern void    nlmclnt_done(struct nlm_host *host);
 extern struct rpc_clnt *nlmclnt_rpc_clnt(struct nlm_host *host);
+extern void    nlmclnt_shutdown_rpc_clnt(struct nlm_host *host);
 
 /*
  * NLM client operations provide a means to modify RPC processing of NLM