FAILED: patch "[PATCH] lockd, nfsd: RCU-protect nlmsvc_ops dispatch" failed to apply to 6.12-stable tree
Subsystems (3)
[email protected]
maintainerThe 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 <[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-6.12.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 '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 <[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 | 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(-)
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); } -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 ||
@@ -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" );
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 */
@@ -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" );
fs/lockd/svcsubs.c +42 -14
84 lines not on this page · raw message
fs/nfsd/lockd.c +4 -2
@@ -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 @@ 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
@@ -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
Signature · 1 line
2.53.0
Re: [PATCH 6.12.y] lockd, nfsd: RCU-protect nlmsvc_ops dispatch
Quoted 256 lines · Sasha Levin
From: Jeff Layton <[email protected]>
[ Upstream commit
641e5e20852359b8c31149be4598884e30652f60]
nlmsvc_opsis published bynfsd_lockd_init()and cleared bynfsd_lockd_shutdown()with plain stores, while lockd dereferences it unguarded from dispatch sites infs/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_opsas__rcu, publish viarcu_assign_pointer(), clear viaRCU_INIT_POINTER()+synchronize_rcu(). Add a struct module *owner field tonlmsvc_bindingand pin the module across indirect calls with try_module_get/module_put. When the binding is torn down, fall back tofput()to avoid leaking struct file references.Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")Assisted-by Claude:claude-opus-4-8Signed-off-by Jeff Layton <[email protected]>Signed-off-by Chuck Lever <[email protected]>[ adapted nlmsvc_ops protection to 6.12’s __be32 fopen API and existing nlm_close_files() helper ]Signed-off-by Sasha Levin <[email protected]>--- 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(-)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); } -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 ||@@ -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" );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 */ @@ -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" );fs/lockd/svcsubs.c +42
-14@@ -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. */ fs/nfsd/lockd.c +4
-2@@ -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 @@ 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@@ -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 Signature · 1 line
2.53.0
Does not apply