FAILED: patch "[PATCH] lockd, nfsd: RCU-protect nlmsvc_ops dispatch" failed to apply to 5.15-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.15-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 <[email protected]>.
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-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 641e5e20852359b8c31149be4598884e30652f60
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090305-accustom-epidermal-06f0@gregkh' --subject-prefix 'PATCH 5.15.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 <[email protected]>
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.
fs/lockd/svc.c +2 -2
@@ -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);
fs/lockd/svc4proc.c +2 -2
@@ -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))
fs/lockd/svcproc.c +2 -2
@@ -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))
fs/lockd/svcsubs.c +45 -7
89 lines not on this page · raw message
fs/nfsd/lockd.c +4 -2
@@ -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 @@ voidnfsd_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 (); }
include/linux/lockd/bind.h +9 -3
@@ -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
Sasha Levin
maintainerPATCH 1/1 lockd, nfsd: RCU-protect nlmsvc_ops dispatch
From: Jeff Layton <[email protected]>
[ 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.
--- fs/lockd/svc.c | 6 ++-- fs/lockd/svc4proc.c | 4 +-- fs/lockd/svcproc.c | 4 +-- fs/lockd/svcsubs.c | 59 ++++++++++++++++++++++++++++---------- fs/nfsd/lockd.c | 6 ++-- include/linux/lockd/bind.h | 12 ++++++-- 6 files changed, 64 insertions(+), 27 deletions(-)
fs/lockd/svc.c +3 -3
@@ -49,7 +49,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);
@@ -113,7 +113,7 @@ static void set_grace_period(struct net *net)static void restart_grace (void ) { -if (nlmsvc_ops) { +if (rcu_access_pointer (nlmsvc_ops)) {struct net *net = &init_net;struct lockd_net *ln =net_generic (net, lockd_net_id);
@@ -175,7 +175,7 @@ lockd(void *vrqstp)svc_process (rqstp); }flush_signals (current); -if (nlmsvc_ops) +if (rcu_access_pointer (nlmsvc_ops))nlmsvc_invalidate_all ();nlm_shutdown_hosts ();cancel_delayed_work_sync (&ln->grace_period_end);
fs/lockd/svc4proc.c +2 -2
@@ -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 ||
@@ -489,7 +489,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" );
fs/lockd/svcproc.c +2 -2
@@ -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 */
@@ -523,7 +523,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" );
fs/lockd/svcsubs.c +44 -15
101 lines not on this page · raw message
fs/nfsd/lockd.c +4 -2
@@ -79,6 +79,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 */ };
@@ -87,11 +88,12 @@ voidnfsd_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 (); }
include/linux/lockd/bind.h +9 -3
@@ -20,11 +20,17 @@/* Dummy declarations */ struct svc_rqst ;struct rpc_task ; +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 **,
@@ -32,7 +38,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
Signature · 1 line
2.53.0