]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
nfsd: check that server is running in unlock_filesystem
authorOlga Kornievskaia <okorniev@redhat.com>
Mon, 15 Dec 2025 19:10:36 +0000 (14:10 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 17 Jan 2026 15:31:15 +0000 (16:31 +0100)
commit d0424066fcd294977f310964bed6f2a487fa4515 upstream.

If we are trying to unlock the filesystem via an administrative
interface and nfsd isn't running, it crashes the server. This
happens currently because nfsd4_revoke_states() access state
structures (eg., conf_id_hashtbl) that has been freed as a part
of the server shutdown.

[   59.465072] Call trace:
[   59.465308]  nfsd4_revoke_states+0x1b4/0x898 [nfsd] (P)
[   59.465830]  write_unlock_fs+0x258/0x440 [nfsd]
[   59.466278]  nfsctl_transaction_write+0xb0/0x120 [nfsd]
[   59.466780]  vfs_write+0x1f0/0x938
[   59.467088]  ksys_write+0xfc/0x1f8
[   59.467395]  __arm64_sys_write+0x74/0xb8
[   59.467746]  invoke_syscall.constprop.0+0xdc/0x1e8
[   59.468177]  do_el0_svc+0x154/0x1d8
[   59.468489]  el0_svc+0x40/0xe0
[   59.468767]  el0t_64_sync_handler+0xa0/0xe8
[   59.469138]  el0t_64_sync+0x1ac/0x1b0

Ensure this can't happen by taking the nfsd_mutex and checking that
the server is still up, and then holding the mutex across the call to
nfsd4_revoke_states().

Reviewed-by: NeilBrown <neil@brown.name>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Fixes: 1ac3629bf0125 ("nfsd: prepare for supporting admin-revocation of state")
Cc: stable@vger.kernel.org
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfsd/nfs4state.c
fs/nfsd/nfsctl.c
fs/nfsd/state.h

index 3c2970ca270f87d07221de720448c3e34a0095fd..1a15e458b178ae5952db7cce1eacb0528f31bbd0 100644 (file)
@@ -1743,7 +1743,7 @@ static struct nfs4_stid *find_one_sb_stid(struct nfs4_client *clp,
 
 /**
  * nfsd4_revoke_states - revoke all nfsv4 states associated with given filesystem
- * @net:  used to identify instance of nfsd (there is one per net namespace)
+ * @nn:   used to identify instance of nfsd (there is one per net namespace)
  * @sb:   super_block used to identify target filesystem
  *
  * All nfs4 states (open, lock, delegation, layout) held by the server instance
@@ -1755,9 +1755,8 @@ static struct nfs4_stid *find_one_sb_stid(struct nfs4_client *clp,
  * The clients which own the states will subsequently being notified that the
  * states have been "admin-revoked".
  */
-void nfsd4_revoke_states(struct net *net, struct super_block *sb)
+void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
 {
-       struct nfsd_net *nn = net_generic(net, nfsd_net_id);
        unsigned int idhashval;
        unsigned int sc_types;
 
index 69a5589c3ceeb6eb26afa07e0f9ea749e8807113..eb012f943912e8d10c99b80851f1799de63d9827 100644 (file)
@@ -262,6 +262,7 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
        struct path path;
        char *fo_path;
        int error;
+       struct nfsd_net *nn;
 
        /* sanity check */
        if (size == 0)
@@ -288,7 +289,13 @@ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
         * 3.  Is that directory the root of an exported file system?
         */
        error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
-       nfsd4_revoke_states(netns(file), path.dentry->d_sb);
+       mutex_lock(&nfsd_mutex);
+       nn = net_generic(netns(file), nfsd_net_id);
+       if (nn->nfsd_serv)
+               nfsd4_revoke_states(nn, path.dentry->d_sb);
+       else
+               error = -EINVAL;
+       mutex_unlock(&nfsd_mutex);
 
        path_put(&path);
        return error;
index 36de46b082e77a2757c9f0a6c2ac3b8612abdcf5..609487d3595980be0017ca5fdf08b007d058a3aa 100644 (file)
@@ -759,9 +759,9 @@ static inline void get_nfs4_file(struct nfs4_file *fi)
 struct nfsd_file *find_any_file(struct nfs4_file *f);
 
 #ifdef CONFIG_NFSD_V4
-void nfsd4_revoke_states(struct net *net, struct super_block *sb);
+void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb);
 #else
-static inline void nfsd4_revoke_states(struct net *net, struct super_block *sb)
+static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
 {
 }
 #endif