]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
NFSD: Prevent post-shutdown use-after-free in NFSD_CMD_UNLOCK_FILESYSTEM
authorChuck Lever <cel@kernel.org>
Sat, 13 Jun 2026 22:16:33 +0000 (18:16 -0400)
committerChuck Lever <cel@kernel.org>
Mon, 6 Jul 2026 01:09:42 +0000 (21:09 -0400)
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>
fs/nfsd/nfsctl.c

index d0486f4a47ba5a9a030ccf05ea7ecbb3de20079c..fa92e31d19d6f78672d8e376079728625d673201 100644 (file)
@@ -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);