All of lore.kernel.org
 help / color / mirror / Atom feed
* FAILED: patch "[PATCH] lockd, nfsd: RCU-protect nlmsvc_ops dispatch" failed to apply to 6.12-stable tree
@ 2026-09-03 13:06 gregkh
  2026-09-05  1:50 ` [PATCH 6.12.y] lockd, nfsd: RCU-protect nlmsvc_ops dispatch Sasha Levin
  0 siblings, 1 reply; 3+ messages in thread
From: gregkh @ 2026-09-03 13:06 UTC (permalink / raw)
  To: jlayton, cel; +Cc: stable


The patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 641e5e20852359b8c31149be4598884e30652f60
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<stable@vger.kernel.org>' --in-reply-to '2026090303-limpness-basis-79a4@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'

Possible dependencies:



thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

From 641e5e20852359b8c31149be4598884e30652f60 Mon Sep 17 00:00:00 2001
From: Jeff Layton <jlayton@kernel.org>
Date: Thu, 11 Jun 2026 16:00:59 -0400
Subject: [PATCH] lockd, nfsd: RCU-protect nlmsvc_ops dispatch

nlmsvc_ops is published by nfsd_lockd_init() and cleared by
nfsd_lockd_shutdown() with plain stores, while lockd dereferences
it unguarded from dispatch sites in fs/lockd/svcsubs.c. The pointer
targets nfsd's .rodata and the fopen/fclose callbacks live in nfsd's
.text, so a stale load after rmmod nfsd results in either a NULL
deref or a module-text use-after-free.

Declare nlmsvc_ops as __rcu, publish via rcu_assign_pointer(), clear
via RCU_INIT_POINTER() + synchronize_rcu(). Add a struct module
*owner field to nlmsvc_binding and pin the module across indirect
calls with try_module_get/module_put. When the binding is torn down,
fall back to fput() to avoid leaking struct file references.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260611-nfsd-testing-v2-16-5b90e276f2d9@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>

diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 490551369ef2..ee90e743064a 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -47,7 +47,7 @@
 
 static struct svc_program	nlmsvc_program;
 
-const struct nlmsvc_binding	*nlmsvc_ops;
+const struct nlmsvc_binding __rcu *nlmsvc_ops;
 EXPORT_SYMBOL_GPL(nlmsvc_ops);
 
 static DEFINE_MUTEX(nlmsvc_mutex);
@@ -142,7 +142,7 @@ lockd(void *vrqstp)
 		nlmsvc_retry_blocked(rqstp);
 		svc_recv(rqstp, 0);
 	}
-	if (nlmsvc_ops)
+	if (rcu_access_pointer(nlmsvc_ops))
 		nlmsvc_invalidate_all();
 	nlm_shutdown_hosts();
 	cancel_delayed_work_sync(&ln->grace_period_end);
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 78e675470c4b..080dffce9d8e 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -128,7 +128,7 @@ nlm4svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
 {
 	struct nlm_host *host;
 
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return NULL;
 	host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
 	if (!host)
@@ -894,7 +894,7 @@ static __be32 nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
 {
 	struct nlm4_res_wrapper *argp = rqstp->rq_argp;
 
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return rpc_success;
 
 	if (nlm4_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 386a881b520f..d410b8c69893 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -133,7 +133,7 @@ nlm3svc_lookup_host(struct svc_rqst *rqstp, string caller, bool monitored)
 {
 	struct nlm_host *host;
 
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return NULL;
 	host = nlmsvc_lookup_host(rqstp, caller.data, caller.len);
 	if (!host)
@@ -924,7 +924,7 @@ static __be32 nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
 {
 	struct nlm_res_wrapper *argp = rqstp->rq_argp;
 
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return rpc_success;
 
 	if (nlm_netobj_to_cookie(&argp->cookie, &argp->xdrgen.cookie))
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index d7ada90dc048..e44eb20d3453 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -90,22 +90,35 @@ int lock_to_openmode(struct file_lock *lock)
 static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
 			   struct nlm_file *file, int mode)
 {
+	const struct nlmsvc_binding *ops;
 	__be32 nlmerr = nlm__int__failed;
 	__be32 deferred = 0;
 	int error;
 	int m;
 
+	rcu_read_lock();
+	ops = rcu_dereference(nlmsvc_ops);
+	if (!ops || !try_module_get(ops->owner)) {
+		rcu_read_unlock();
+		return nlm__int__failed;
+	}
+	rcu_read_unlock();
+
 	for (m = O_RDONLY; m <= O_WRONLY; m++) {
 		struct file **fp = &file->f_file[m];
 
 		if (mode != O_RDWR && mode != m)
 			continue;
-		if (*fp)
+		if (*fp) {
+			module_put(ops->owner);
 			return nlm_granted;
+		}
 
-		error = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, m);
-		if (!error)
+		error = ops->fopen(rqstp, &file->f_handle, fp, m);
+		if (!error) {
+			module_put(ops->owner);
 			return nlm_granted;
+		}
 
 		dprintk("lockd: open failed (errno %d)\n", error);
 		switch (error) {
@@ -122,6 +135,7 @@ static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
 		}
 	}
 
+	module_put(ops->owner);
 	return deferred ? deferred : nlmerr;
 }
 
@@ -185,6 +199,33 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
 	goto out_unlock;
 }
 
+/*
+ * Release the struct file references held by a nlm_file.
+ */
+static void nlm_release_files(struct nlm_file *file)
+{
+	const struct nlmsvc_binding *ops;
+	bool have_ops;
+
+	rcu_read_lock();
+	ops = rcu_dereference(nlmsvc_ops);
+	have_ops = ops && try_module_get(ops->owner);
+	rcu_read_unlock();
+
+	if (have_ops) {
+		if (file->f_file[O_RDONLY])
+			ops->fclose(file->f_file[O_RDONLY]);
+		if (file->f_file[O_WRONLY])
+			ops->fclose(file->f_file[O_WRONLY]);
+		module_put(ops->owner);
+	} else {
+		if (file->f_file[O_RDONLY])
+			fput(file->f_file[O_RDONLY]);
+		if (file->f_file[O_WRONLY])
+			fput(file->f_file[O_WRONLY]);
+	}
+}
+
 /*
  * Delete a file after having released all locks, blocks and shares
  */
@@ -194,10 +235,7 @@ nlm_delete_file(struct nlm_file *file)
 	nlm_debug_print_file("closing file", file);
 	if (!hlist_unhashed(&file->f_list)) {
 		hlist_del(&file->f_list);
-		if (file->f_file[O_RDONLY])
-			nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
-		if (file->f_file[O_WRONLY])
-			nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
+		nlm_release_files(file);
 		kfree(file);
 	} else {
 		printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 6fe1325815e0..72a5b499839d 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -92,6 +92,7 @@ nlm_fclose(struct file *filp)
 }
 
 static const struct nlmsvc_binding nfsd_nlm_ops = {
+	.owner		= THIS_MODULE,
 	.fopen		= nlm_fopen,		/* open file for locking */
 	.fclose		= nlm_fclose,		/* close file */
 };
@@ -100,11 +101,12 @@ void
 nfsd_lockd_init(void)
 {
 	dprintk("nfsd: initializing lockd\n");
-	nlmsvc_ops = &nfsd_nlm_ops;
+	rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops);
 }
 
 void
 nfsd_lockd_shutdown(void)
 {
-	nlmsvc_ops = NULL;
+	RCU_INIT_POINTER(nlmsvc_ops, NULL);
+	synchronize_rcu();
 }
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index b614e0deea72..db8207d4059f 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -16,17 +16,23 @@ struct svc_rqst;
 struct rpc_task;
 struct rpc_clnt;
 struct super_block;
+struct module;
 
-/*
- * This is the set of functions for lockd->nfsd communication
+/**
+ * struct nlmsvc_binding - lockd -> nfsd callback table
+ * @owner:  module that provides this binding.
+ * @fopen:  open a file by NFS file handle on behalf of an NLM request.
+ * @fclose: close a file that was previously opened via @fopen.
+ *          Implementations MUST be semantically equivalent to fput().
  */
 struct nlmsvc_binding {
+	struct module	*owner;
 	int		(*fopen)(struct svc_rqst *rqstp, struct nfs_fh *f,
 				 struct file **filp, int flags);
 	void		(*fclose)(struct file *filp);
 };
 
-extern const struct nlmsvc_binding *nlmsvc_ops;
+extern const struct nlmsvc_binding __rcu *nlmsvc_ops;
 
 /*
  * Similar to nfs_client_initdata, but without the NFS-specific


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 6.12.y] lockd, nfsd: RCU-protect nlmsvc_ops dispatch
  2026-09-03 13:06 FAILED: patch "[PATCH] lockd, nfsd: RCU-protect nlmsvc_ops dispatch" failed to apply to 6.12-stable tree gregkh
@ 2026-09-05  1:50 ` Sasha Levin
  2026-09-17 11:29   ` Greg KH
  0 siblings, 1 reply; 3+ messages in thread
From: Sasha Levin @ 2026-09-05  1:50 UTC (permalink / raw)
  To: stable; +Cc: Jeff Layton, Chuck Lever, Sasha Levin

From: Jeff Layton <jlayton@kernel.org>

[ Upstream commit 641e5e20852359b8c31149be4598884e30652f60 ]

nlmsvc_ops is published by nfsd_lockd_init() and cleared by
nfsd_lockd_shutdown() with plain stores, while lockd dereferences
it unguarded from dispatch sites in fs/lockd/svcsubs.c. The pointer
targets nfsd's .rodata and the fopen/fclose callbacks live in nfsd's
.text, so a stale load after rmmod nfsd results in either a NULL
deref or a module-text use-after-free.

Declare nlmsvc_ops as __rcu, publish via rcu_assign_pointer(), clear
via RCU_INIT_POINTER() + synchronize_rcu(). Add a struct module
*owner field to nlmsvc_binding and pin the module across indirect
calls with try_module_get/module_put. When the binding is torn down,
fall back to fput() to avoid leaking struct file references.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260611-nfsd-testing-v2-16-5b90e276f2d9@kernel.org
Signed-off-by: Chuck Lever <cel@kernel.org>
[ adapted nlmsvc_ops protection to 6.12’s __be32 fopen API and existing nlm_close_files() helper ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/lockd/svc.c             |  4 +--
 fs/lockd/svc4proc.c        |  4 +--
 fs/lockd/svcproc.c         |  4 +--
 fs/lockd/svcsubs.c         | 56 ++++++++++++++++++++++++++++----------
 fs/nfsd/lockd.c            |  6 ++--
 include/linux/lockd/bind.h | 12 ++++++--
 6 files changed, 61 insertions(+), 25 deletions(-)

diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
index 4ec22c2f2ea3c..87f20d1edb2c2 100644
--- a/fs/lockd/svc.c
+++ b/fs/lockd/svc.c
@@ -47,7 +47,7 @@
 
 static struct svc_program	nlmsvc_program;
 
-const struct nlmsvc_binding	*nlmsvc_ops;
+const struct nlmsvc_binding __rcu *nlmsvc_ops;
 EXPORT_SYMBOL_GPL(nlmsvc_ops);
 
 static DEFINE_MUTEX(nlmsvc_mutex);
@@ -142,7 +142,7 @@ lockd(void *vrqstp)
 		nlmsvc_retry_blocked(rqstp);
 		svc_recv(rqstp);
 	}
-	if (nlmsvc_ops)
+	if (rcu_access_pointer(nlmsvc_ops))
 		nlmsvc_invalidate_all();
 	nlm_shutdown_hosts();
 	cancel_delayed_work_sync(&ln->grace_period_end);
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 3f633bb24623a..ea3b689f8b93a 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -29,7 +29,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
 	__be32			error = 0;
 
 	/* nfsd callbacks must have been installed for this procedure */
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return nlm_lck_denied_nolocks;
 
 	if (lock->lock_start > OFFSET_MAX ||
@@ -487,7 +487,7 @@ nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
 {
 	struct nlm_res *argp = rqstp->rq_argp;
 
-        if (!nlmsvc_ops)
+        if (!rcu_access_pointer(nlmsvc_ops))
                 return rpc_success;
 
         dprintk("lockd: GRANTED_RES   called\n");
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index 8cda2aba82d25..44da63392a09d 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -59,7 +59,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
 	__be32			error = 0;
 
 	/* nfsd callbacks must have been installed for this procedure */
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return nlm_lck_denied_nolocks;
 
 	/* Obtain host handle */
@@ -521,7 +521,7 @@ nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
 {
 	struct nlm_res *argp = rqstp->rq_argp;
 
-	if (!nlmsvc_ops)
+	if (!rcu_access_pointer(nlmsvc_ops))
 		return rpc_success;
 
 	dprintk("lockd: GRANTED_RES   called\n");
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
index 1a97f69397c00..396436eac8e22 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -86,12 +86,24 @@ int lock_to_openmode(struct file_lock *lock)
 static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
 			   struct nlm_file *file, int mode)
 {
+	const struct nlmsvc_binding *ops;
 	struct file **fp = &file->f_file[mode];
 	__be32	nfserr;
 
-	if (*fp)
+	rcu_read_lock();
+	ops = rcu_dereference(nlmsvc_ops);
+	if (!ops || !try_module_get(ops->owner)) {
+		rcu_read_unlock();
+		return nlm_lck_denied_nolocks;
+	}
+	rcu_read_unlock();
+
+	if (*fp) {
+		module_put(ops->owner);
 		return 0;
-	nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
+	}
+	nfserr = ops->fopen(rqstp, &file->f_handle, fp, mode);
+	module_put(ops->owner);
 	if (nfserr)
 		dprintk("lockd: open failed (error %d)\n", nfserr);
 	return nfserr;
@@ -159,6 +171,33 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
 	goto out_unlock;
 }
 
+/*
+ * Release the struct file references held by a nlm_file.
+ */
+static void nlm_close_files(struct nlm_file *file)
+{
+	const struct nlmsvc_binding *ops;
+	bool have_ops;
+
+	rcu_read_lock();
+	ops = rcu_dereference(nlmsvc_ops);
+	have_ops = ops && try_module_get(ops->owner);
+	rcu_read_unlock();
+
+	if (have_ops) {
+		if (file->f_file[O_RDONLY])
+			ops->fclose(file->f_file[O_RDONLY]);
+		if (file->f_file[O_WRONLY])
+			ops->fclose(file->f_file[O_WRONLY]);
+		module_put(ops->owner);
+	} else {
+		if (file->f_file[O_RDONLY])
+			fput(file->f_file[O_RDONLY]);
+		if (file->f_file[O_WRONLY])
+			fput(file->f_file[O_WRONLY]);
+	}
+}
+
 /*
  * Delete a file after having released all locks, blocks and shares
  */
@@ -168,10 +207,7 @@ nlm_delete_file(struct nlm_file *file)
 	nlm_debug_print_file("closing file", file);
 	if (!hlist_unhashed(&file->f_list)) {
 		hlist_del(&file->f_list);
-		if (file->f_file[O_RDONLY])
-			nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
-		if (file->f_file[O_WRONLY])
-			nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
+		nlm_close_files(file);
 		kfree(file);
 	} else {
 		printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
@@ -286,14 +322,6 @@ nlm_file_inuse(struct nlm_file *file)
 	return 0;
 }
 
-static void nlm_close_files(struct nlm_file *file)
-{
-	if (file->f_file[O_RDONLY])
-		nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
-	if (file->f_file[O_WRONLY])
-		nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
-}
-
 /*
  * Loop over all files in the file table.
  */
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
index 6b042218668b8..e933788e7a350 100644
--- a/fs/nfsd/lockd.c
+++ b/fs/nfsd/lockd.c
@@ -88,6 +88,7 @@ nlm_fclose(struct file *filp)
 }
 
 static const struct nlmsvc_binding nfsd_nlm_ops = {
+	.owner		= THIS_MODULE,
 	.fopen		= nlm_fopen,		/* open file for locking */
 	.fclose		= nlm_fclose,		/* close file */
 };
@@ -96,11 +97,12 @@ void
 nfsd_lockd_init(void)
 {
 	dprintk("nfsd: initializing lockd\n");
-	nlmsvc_ops = &nfsd_nlm_ops;
+	rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops);
 }
 
 void
 nfsd_lockd_shutdown(void)
 {
-	nlmsvc_ops = NULL;
+	RCU_INIT_POINTER(nlmsvc_ops, NULL);
+	synchronize_rcu();
 }
diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
index c53c81242e727..e25943e485c5c 100644
--- a/include/linux/lockd/bind.h
+++ b/include/linux/lockd/bind.h
@@ -21,11 +21,17 @@
 struct svc_rqst;
 struct rpc_task;
 struct rpc_clnt;
+struct module;
 
-/*
- * This is the set of functions for lockd->nfsd communication
+/**
+ * struct nlmsvc_binding - lockd -> nfsd callback table
+ * @owner:  module that provides this binding.
+ * @fopen:  open a file by NFS file handle on behalf of an NLM request.
+ * @fclose: close a file that was previously opened via @fopen.
+ *          Implementations MUST be semantically equivalent to fput().
  */
 struct nlmsvc_binding {
+	struct module		*owner;
 	__be32			(*fopen)(struct svc_rqst *,
 						struct nfs_fh *,
 						struct file **,
@@ -33,7 +39,7 @@ struct nlmsvc_binding {
 	void			(*fclose)(struct file *);
 };
 
-extern const struct nlmsvc_binding *nlmsvc_ops;
+extern const struct nlmsvc_binding __rcu *nlmsvc_ops;
 
 /*
  * Similar to nfs_client_initdata, but without the NFS-specific
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 6.12.y] lockd, nfsd: RCU-protect nlmsvc_ops dispatch
  2026-09-05  1:50 ` [PATCH 6.12.y] lockd, nfsd: RCU-protect nlmsvc_ops dispatch Sasha Levin
@ 2026-09-17 11:29   ` Greg KH
  0 siblings, 0 replies; 3+ messages in thread
From: Greg KH @ 2026-09-17 11:29 UTC (permalink / raw)
  To: Sasha Levin; +Cc: stable, Jeff Layton, Chuck Lever

On Fri, Sep 04, 2026 at 09:50:27PM -0400, Sasha Levin wrote:
> From: Jeff Layton <jlayton@kernel.org>
> 
> [ Upstream commit 641e5e20852359b8c31149be4598884e30652f60 ]
> 
> nlmsvc_ops is published by nfsd_lockd_init() and cleared by
> nfsd_lockd_shutdown() with plain stores, while lockd dereferences
> it unguarded from dispatch sites in fs/lockd/svcsubs.c. The pointer
> targets nfsd's .rodata and the fopen/fclose callbacks live in nfsd's
> .text, so a stale load after rmmod nfsd results in either a NULL
> deref or a module-text use-after-free.
> 
> Declare nlmsvc_ops as __rcu, publish via rcu_assign_pointer(), clear
> via RCU_INIT_POINTER() + synchronize_rcu(). Add a struct module
> *owner field to nlmsvc_binding and pin the module across indirect
> calls with try_module_get/module_put. When the binding is torn down,
> fall back to fput() to avoid leaking struct file references.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> Link: https://patch.msgid.link/20260611-nfsd-testing-v2-16-5b90e276f2d9@kernel.org
> Signed-off-by: Chuck Lever <cel@kernel.org>
> [ adapted nlmsvc_ops protection to 6.12’s __be32 fopen API and existing nlm_close_files() helper ]
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
>  fs/lockd/svc.c             |  4 +--
>  fs/lockd/svc4proc.c        |  4 +--
>  fs/lockd/svcproc.c         |  4 +--
>  fs/lockd/svcsubs.c         | 56 ++++++++++++++++++++++++++++----------
>  fs/nfsd/lockd.c            |  6 ++--
>  include/linux/lockd/bind.h | 12 ++++++--
>  6 files changed, 61 insertions(+), 25 deletions(-)
> 
> diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
> index 4ec22c2f2ea3c..87f20d1edb2c2 100644
> --- a/fs/lockd/svc.c
> +++ b/fs/lockd/svc.c
> @@ -47,7 +47,7 @@
>  
>  static struct svc_program	nlmsvc_program;
>  
> -const struct nlmsvc_binding	*nlmsvc_ops;
> +const struct nlmsvc_binding __rcu *nlmsvc_ops;
>  EXPORT_SYMBOL_GPL(nlmsvc_ops);
>  
>  static DEFINE_MUTEX(nlmsvc_mutex);
> @@ -142,7 +142,7 @@ lockd(void *vrqstp)
>  		nlmsvc_retry_blocked(rqstp);
>  		svc_recv(rqstp);
>  	}
> -	if (nlmsvc_ops)
> +	if (rcu_access_pointer(nlmsvc_ops))
>  		nlmsvc_invalidate_all();
>  	nlm_shutdown_hosts();
>  	cancel_delayed_work_sync(&ln->grace_period_end);
> diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
> index 3f633bb24623a..ea3b689f8b93a 100644
> --- a/fs/lockd/svc4proc.c
> +++ b/fs/lockd/svc4proc.c
> @@ -29,7 +29,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
>  	__be32			error = 0;
>  
>  	/* nfsd callbacks must have been installed for this procedure */
> -	if (!nlmsvc_ops)
> +	if (!rcu_access_pointer(nlmsvc_ops))
>  		return nlm_lck_denied_nolocks;
>  
>  	if (lock->lock_start > OFFSET_MAX ||
> @@ -487,7 +487,7 @@ nlm4svc_proc_granted_res(struct svc_rqst *rqstp)
>  {
>  	struct nlm_res *argp = rqstp->rq_argp;
>  
> -        if (!nlmsvc_ops)
> +        if (!rcu_access_pointer(nlmsvc_ops))
>                  return rpc_success;
>  
>          dprintk("lockd: GRANTED_RES   called\n");
> diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
> index 8cda2aba82d25..44da63392a09d 100644
> --- a/fs/lockd/svcproc.c
> +++ b/fs/lockd/svcproc.c
> @@ -59,7 +59,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
>  	__be32			error = 0;
>  
>  	/* nfsd callbacks must have been installed for this procedure */
> -	if (!nlmsvc_ops)
> +	if (!rcu_access_pointer(nlmsvc_ops))
>  		return nlm_lck_denied_nolocks;
>  
>  	/* Obtain host handle */
> @@ -521,7 +521,7 @@ nlmsvc_proc_granted_res(struct svc_rqst *rqstp)
>  {
>  	struct nlm_res *argp = rqstp->rq_argp;
>  
> -	if (!nlmsvc_ops)
> +	if (!rcu_access_pointer(nlmsvc_ops))
>  		return rpc_success;
>  
>  	dprintk("lockd: GRANTED_RES   called\n");
> diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
> index 1a97f69397c00..396436eac8e22 100644
> --- a/fs/lockd/svcsubs.c
> +++ b/fs/lockd/svcsubs.c
> @@ -86,12 +86,24 @@ int lock_to_openmode(struct file_lock *lock)
>  static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
>  			   struct nlm_file *file, int mode)
>  {
> +	const struct nlmsvc_binding *ops;
>  	struct file **fp = &file->f_file[mode];
>  	__be32	nfserr;
>  
> -	if (*fp)
> +	rcu_read_lock();
> +	ops = rcu_dereference(nlmsvc_ops);
> +	if (!ops || !try_module_get(ops->owner)) {
> +		rcu_read_unlock();
> +		return nlm_lck_denied_nolocks;
> +	}
> +	rcu_read_unlock();
> +
> +	if (*fp) {
> +		module_put(ops->owner);
>  		return 0;
> -	nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
> +	}
> +	nfserr = ops->fopen(rqstp, &file->f_handle, fp, mode);
> +	module_put(ops->owner);
>  	if (nfserr)
>  		dprintk("lockd: open failed (error %d)\n", nfserr);
>  	return nfserr;
> @@ -159,6 +171,33 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
>  	goto out_unlock;
>  }
>  
> +/*
> + * Release the struct file references held by a nlm_file.
> + */
> +static void nlm_close_files(struct nlm_file *file)
> +{
> +	const struct nlmsvc_binding *ops;
> +	bool have_ops;
> +
> +	rcu_read_lock();
> +	ops = rcu_dereference(nlmsvc_ops);
> +	have_ops = ops && try_module_get(ops->owner);
> +	rcu_read_unlock();
> +
> +	if (have_ops) {
> +		if (file->f_file[O_RDONLY])
> +			ops->fclose(file->f_file[O_RDONLY]);
> +		if (file->f_file[O_WRONLY])
> +			ops->fclose(file->f_file[O_WRONLY]);
> +		module_put(ops->owner);
> +	} else {
> +		if (file->f_file[O_RDONLY])
> +			fput(file->f_file[O_RDONLY]);
> +		if (file->f_file[O_WRONLY])
> +			fput(file->f_file[O_WRONLY]);
> +	}
> +}
> +
>  /*
>   * Delete a file after having released all locks, blocks and shares
>   */
> @@ -168,10 +207,7 @@ nlm_delete_file(struct nlm_file *file)
>  	nlm_debug_print_file("closing file", file);
>  	if (!hlist_unhashed(&file->f_list)) {
>  		hlist_del(&file->f_list);
> -		if (file->f_file[O_RDONLY])
> -			nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
> -		if (file->f_file[O_WRONLY])
> -			nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
> +		nlm_close_files(file);
>  		kfree(file);
>  	} else {
>  		printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
> @@ -286,14 +322,6 @@ nlm_file_inuse(struct nlm_file *file)
>  	return 0;
>  }
>  
> -static void nlm_close_files(struct nlm_file *file)
> -{
> -	if (file->f_file[O_RDONLY])
> -		nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
> -	if (file->f_file[O_WRONLY])
> -		nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
> -}
> -
>  /*
>   * Loop over all files in the file table.
>   */
> diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
> index 6b042218668b8..e933788e7a350 100644
> --- a/fs/nfsd/lockd.c
> +++ b/fs/nfsd/lockd.c
> @@ -88,6 +88,7 @@ nlm_fclose(struct file *filp)
>  }
>  
>  static const struct nlmsvc_binding nfsd_nlm_ops = {
> +	.owner		= THIS_MODULE,
>  	.fopen		= nlm_fopen,		/* open file for locking */
>  	.fclose		= nlm_fclose,		/* close file */
>  };
> @@ -96,11 +97,12 @@ void
>  nfsd_lockd_init(void)
>  {
>  	dprintk("nfsd: initializing lockd\n");
> -	nlmsvc_ops = &nfsd_nlm_ops;
> +	rcu_assign_pointer(nlmsvc_ops, &nfsd_nlm_ops);
>  }
>  
>  void
>  nfsd_lockd_shutdown(void)
>  {
> -	nlmsvc_ops = NULL;
> +	RCU_INIT_POINTER(nlmsvc_ops, NULL);
> +	synchronize_rcu();
>  }
> diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h
> index c53c81242e727..e25943e485c5c 100644
> --- a/include/linux/lockd/bind.h
> +++ b/include/linux/lockd/bind.h
> @@ -21,11 +21,17 @@
>  struct svc_rqst;
>  struct rpc_task;
>  struct rpc_clnt;
> +struct module;
>  
> -/*
> - * This is the set of functions for lockd->nfsd communication
> +/**
> + * struct nlmsvc_binding - lockd -> nfsd callback table
> + * @owner:  module that provides this binding.
> + * @fopen:  open a file by NFS file handle on behalf of an NLM request.
> + * @fclose: close a file that was previously opened via @fopen.
> + *          Implementations MUST be semantically equivalent to fput().
>   */
>  struct nlmsvc_binding {
> +	struct module		*owner;
>  	__be32			(*fopen)(struct svc_rqst *,
>  						struct nfs_fh *,
>  						struct file **,
> @@ -33,7 +39,7 @@ struct nlmsvc_binding {
>  	void			(*fclose)(struct file *);
>  };
>  
> -extern const struct nlmsvc_binding *nlmsvc_ops;
> +extern const struct nlmsvc_binding __rcu *nlmsvc_ops;
>  
>  /*
>   * Similar to nfs_client_initdata, but without the NFS-specific
> -- 
> 2.53.0
> 
> 

Does not apply

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-17 11:32 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 13:06 FAILED: patch "[PATCH] lockd, nfsd: RCU-protect nlmsvc_ops dispatch" failed to apply to 6.12-stable tree gregkh
2026-09-05  1:50 ` [PATCH 6.12.y] lockd, nfsd: RCU-protect nlmsvc_ops dispatch Sasha Levin
2026-09-17 11:29   ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.