* 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.