summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChuck Lever <cel@kernel.org>2026-06-13 18:16:33 -0400
committerChuck Lever <cel@kernel.org>2026-07-05 21:09:42 -0400
commit92ea163c773cb4d0d5eaf103ed80c49d6758b96f (patch)
tree06a4a9b8093da0e412238c181db21cb095284021
parente5248a7426030db1e126363f72afdb3b71339a5c (diff)
downloadlinux-next-92ea163c773cb4d0d5eaf103ed80c49d6758b96f.tar.gz
linux-next-92ea163c773cb4d0d5eaf103ed80c49d6758b96f.zip
NFSD: Prevent post-shutdown use-after-free in NFSD_CMD_UNLOCK_FILESYSTEM
The NFSD_CMD_UNLOCK_FILESYSTEM netlink command runs nfsd4_cancel_copy_by_sb() before nfsd_mutex is held and before nn->nfsd_serv is confirmed set, the same pre-mutex ordering the procfs unlock_filesystem path carried. Once nfsd has shut down, nfs4_state_destroy_net() has freed nn->conf_id_hashtbl but left the pointer intact, so the cancel helper iterates freed slab memory as an array of struct list_head and then dereferences a bogus nfs4_client when it takes clp->async_lock. A local administrator holding CAP_SYS_ADMIN can reach this use-after-free by stopping the server and then issuing the command. Move the async COPY cancel into the nfsd_mutex section, after nn->nfsd_serv is confirmed, so every NFSv4 state-table walker on this path observes a running server. Async copies exist only while the server runs, so gating the cancel on nn->nfsd_serv loses nothing. Fixes: 327c5168eff2 ("NFSD: Add NFSD_CMD_UNLOCK_FILESYSTEM netlink command") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260613-unlock-filesystem-uaf-v1-2-462b9bec8c84@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
-rw-r--r--fs/nfsd/nfsctl.c7
1 files changed, 4 insertions, 3 deletions
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index d0486f4a47ba..fa92e31d19d6 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -2340,14 +2340,15 @@ int nfsd_nl_unlock_filesystem_doit(struct sk_buff *skb,
if (error)
return error;
- nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb);
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
mutex_lock(&nfsd_mutex);
- if (nn->nfsd_serv)
+ if (nn->nfsd_serv) {
+ nfsd4_cancel_copy_by_sb(net, path.dentry->d_sb);
nfsd4_revoke_states(nn, path.dentry->d_sb);
- else
+ } else {
error = -EINVAL;
+ }
mutex_unlock(&nfsd_mutex);
path_put(&path);