From: Jeff Layton <jlayton@kernel.org>
To: Chuck Lever <chuck.lever@oracle.com>, NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
Lorenzo Bianconi <lorenzo@kernel.org>,
Anna Schumaker <anna.schumaker@oracle.com>,
Trond Myklebust <trondmy@kernel.org>,
Anna Schumaker <anna@kernel.org>,
Mike Snitzer <snitzer@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>, Chris Mason <clm@meta.com>,
linux-nfs@vger.kernel.org, linux-kernel@vger.kernel.org,
Trond Myklebust <trond.myklebust@hammerspace.com>,
Jeff Layton <jlayton@kernel.org>
Subject: [PATCH v2 8/9] nfsd: hold net namespace reference for delayed-dispose nfsd_files
Date: Tue, 02 Jun 2026 12:23:20 -0400 [thread overview]
Message-ID: <20260602-nfsd-testing-v2-8-e4ea62e3cd5c@kernel.org> (raw)
In-Reply-To: <20260602-nfsd-testing-v2-0-e4ea62e3cd5c@kernel.org>
Take a net-namespace reference in nfsd_file_dispose_list_delayed()
(get_net) and release it in nfsd_file_free() (put_net), so that
nf_net is always valid for files that the GC or shrinker has isolated
from the hash table and LRU -- which __nfsd_file_cache_purge() cannot
see.
Without this, nf_net can dangle for in-flight files whose net namespace
is torn down concurrently, causing a use-after-free when
nfsd_file_dispose_list_delayed() calls net_generic(nf->nf_net, ...).
Only files entering the delayed-dispose path need the net reference;
files freed synchronously (non-GC stateful opens, purge, direct put)
are always freed while the net namespace is still alive, so they skip
get_net()/put_net() entirely. A new NFSD_FILE_NET_HELD flag tracks
whether a given nfsd_file holds a net reference.
Because nfsd_file_free() may now call put_net(nf->nf_net), the old
nfsd_file_put_local() pattern of returning nf->nf_net after
nfsd_file_put() is unsafe -- put_net() could theoretically drop the
last net namespace reference, leaving the returned pointer stale.
Fix this by moving the nfsd_net_put() call into nfsd_file_put_local()
itself, before the nfsd_file_put() that may trigger nfsd_file_free().
The function now returns void and the caller no longer needs to handle
the net reference.
Fixes: 43fd953fa7e2 ("nfsd: simplify the delayed disposal list code")
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
fs/nfsd/filecache.c | 34 ++++++++++++++++++++++++++--------
fs/nfsd/filecache.h | 3 ++-
fs/nfsd/localio.c | 4 ++--
include/linux/nfslocalio.h | 9 ++-------
4 files changed, 32 insertions(+), 18 deletions(-)
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 03f01a0beced..957fe57be063 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -295,6 +295,9 @@ nfsd_file_free(struct nfsd_file *nf)
if (WARN_ON_ONCE(!list_empty(&nf->nf_lru)))
return;
+ if (test_bit(NFSD_FILE_NET_HELD, &nf->nf_flags))
+ put_net(nf->nf_net);
+
call_rcu(&nf->nf_rcu, nfsd_file_slab_free);
}
@@ -375,24 +378,28 @@ nfsd_file_put(struct nfsd_file *nf)
}
/**
- * nfsd_file_put_local - put nfsd_file reference and arm nfsd_net_put in caller
+ * nfsd_file_put_local - put nfsd_file reference and release nfsd_net ref
* @pnf: nfsd_file of which to put the reference
*
- * First save the associated net to return to caller, then put
- * the reference of the nfsd_file.
+ * Drops both the nfsd_file reference and the associated nfsd_net
+ * reference. The nfsd_net ref is released before the file ref so
+ * that put_net() inside nfsd_file_free() cannot drop the last net
+ * namespace reference while the caller still needs it.
*/
-struct net *
+void
nfsd_file_put_local(struct nfsd_file __rcu **pnf)
{
struct nfsd_file *nf;
- struct net *net = NULL;
nf = unrcu_pointer(xchg(pnf, NULL));
if (nf) {
- net = nf->nf_net;
+ struct net *net = nf->nf_net;
+
+ rcu_read_lock();
+ nfsd_net_put(net);
+ rcu_read_unlock();
nfsd_file_put(nf);
}
- return net;
}
/**
@@ -433,9 +440,20 @@ nfsd_file_dispose_list_delayed(struct list_head *dispose)
while (!list_empty(dispose)) {
struct nfsd_file *nf = list_first_entry(dispose,
struct nfsd_file, nf_gc);
- struct nfsd_net *nn = net_generic(nf->nf_net, nfsd_net_id);
+ struct nfsd_net *nn;
struct svc_serv *serv;
+ /*
+ * Pin the net namespace so nf_net stays valid while the
+ * file sits on the per-net dispose list. Callers (the GC
+ * worker, shrinker, and fsnotify callbacks) always run
+ * before nfsd_net_exit(), so nf_net is still live here.
+ * The matching put_net() is in nfsd_file_free().
+ */
+ get_net(nf->nf_net);
+ set_bit(NFSD_FILE_NET_HELD, &nf->nf_flags);
+
+ nn = net_generic(nf->nf_net, nfsd_net_id);
spin_lock(&nn->fcache_dispose_lock);
list_move_tail(&nf->nf_gc, &nn->fcache_dispose_list);
spin_unlock(&nn->fcache_dispose_lock);
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index 683b6437cacc..7ae3c0ea0a2a 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -45,6 +45,7 @@ struct nfsd_file {
#define NFSD_FILE_REFERENCED (2)
#define NFSD_FILE_GC (3)
#define NFSD_FILE_RECENT (4)
+#define NFSD_FILE_NET_HELD (5)
unsigned long nf_flags;
refcount_t nf_ref;
unsigned char nf_may;
@@ -66,7 +67,7 @@ void nfsd_file_cache_shutdown(void);
int nfsd_file_cache_start_net(struct net *net);
void nfsd_file_cache_shutdown_net(struct net *net);
void nfsd_file_put(struct nfsd_file *nf);
-struct net *nfsd_file_put_local(struct nfsd_file __rcu **nf);
+void nfsd_file_put_local(struct nfsd_file __rcu **nf);
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
struct file *nfsd_file_file(struct nfsd_file *nf);
void nfsd_file_close_inode_sync(struct inode *inode);
diff --git a/fs/nfsd/localio.c b/fs/nfsd/localio.c
index c3eb0557b3e1..e3295bae75a4 100644
--- a/fs/nfsd/localio.c
+++ b/fs/nfsd/localio.c
@@ -40,8 +40,8 @@
* avoid all the NFS overhead with reads, writes and commits.
*
* On successful return, returned nfsd_file will have its nf_net member
- * set. Caller (NFS client) is responsible for calling nfsd_net_put and
- * nfsd_file_put (via nfs_to_nfsd_file_put_local).
+ * set. Caller (NFS client) is responsible for calling nfsd_file_put
+ * (via nfs_to_nfsd_file_put_local), which also releases the nfsd_net ref.
*/
static struct nfsd_file *
nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
diff --git a/include/linux/nfslocalio.h b/include/linux/nfslocalio.h
index 3d91043254e6..7267a69092d1 100644
--- a/include/linux/nfslocalio.h
+++ b/include/linux/nfslocalio.h
@@ -62,7 +62,7 @@ struct nfsd_localio_operations {
const struct nfs_fh *,
struct nfsd_file __rcu **pnf,
const fmode_t);
- struct net *(*nfsd_file_put_local)(struct nfsd_file __rcu **);
+ void (*nfsd_file_put_local)(struct nfsd_file __rcu **);
struct file *(*nfsd_file_file)(struct nfsd_file *);
void (*nfsd_file_dio_alignment)(struct nfsd_file *,
u32 *, u32 *, u32 *);
@@ -96,12 +96,7 @@ static inline void nfs_to_nfsd_file_put_local(struct nfsd_file __rcu **localio)
* must prevent nfsd shutdown from completing as nfs_close_local_fh()
* does by blocking the nfs_uuid from being finally put.
*/
- struct net *net;
-
- net = nfs_to->nfsd_file_put_local(localio);
-
- if (net)
- nfs_to_nfsd_net_put(net);
+ nfs_to->nfsd_file_put_local(localio);
}
#else /* CONFIG_NFS_LOCALIO */
--
2.54.0
next prev parent reply other threads:[~2026-06-02 16:23 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-02 16:23 [PATCH v2 0/9] nfsd: fixes for locally-triggerable bugs Jeff Layton
2026-06-02 16:23 ` [PATCH v2 1/9] nfsd: defer vfree of compound ops to fix rpc_status UAF Jeff Layton
2026-06-02 16:23 ` [PATCH v2 2/9] nfsd: hold rcu across localio cmpxchg retry Jeff Layton
2026-06-02 16:23 ` [PATCH v2 3/9] nfs/localio: fix ref leak on nfs_uuid_add_file failure Jeff Layton
2026-06-02 16:23 ` [PATCH v2 4/9] nfsd: guard nfsd_serv deref in nfsd_file_net_dispose Jeff Layton
2026-06-02 16:23 ` [PATCH v2 5/9] nfsd: widen nfsd_genl_rqstp address fields to sockaddr_storage Jeff Layton
2026-06-02 16:23 ` [PATCH v2 6/9] nfsd: fix refcount leak in nfsd_file_lru_add on insertion failure Jeff Layton
2026-06-02 16:23 ` [PATCH v2 7/9] nfsd: fix fcache_disposal UAF by inlining dispose state into nfsd_net Jeff Layton
2026-06-02 16:23 ` Jeff Layton [this message]
2026-06-03 17:33 ` [PATCH v2 8/9] nfsd: hold net namespace reference for delayed-dispose nfsd_files Chuck Lever
2026-06-03 17:50 ` Jeff Layton
2026-06-03 18:20 ` Chuck Lever
2026-06-03 19:15 ` Jeff Layton
2026-06-02 16:23 ` [PATCH v2 9/9] nfsd: unify cleanups in nfsd_cross_mnt() exits Jeff Layton
2026-06-03 20:30 ` [PATCH v2 0/9] nfsd: fixes for locally-triggerable bugs Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260602-nfsd-testing-v2-8-e4ea62e3cd5c@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=anna.schumaker@oracle.com \
--cc=anna@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=clm@meta.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=lorenzo@kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=snitzer@kernel.org \
--cc=tom@talpey.com \
--cc=trond.myklebust@hammerspace.com \
--cc=trondmy@kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.