NFS: nfs_delegation_find_inode_server must first reference the superblock
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sun, 10 Jan 2021 20:46:06 +0000 (15:46 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Sun, 10 Jan 2021 21:29:28 +0000 (16:29 -0500)
Before referencing the inode, we must ensure that the superblock can be
referenced. Otherwise, we can end up with iput() calling superblock
operations that are no longer valid or accessible.

Fixes: e39d8a186ed0 ("NFSv4: Fix an Oops during delegation callbacks")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/delegation.c

index 816e142..04bf806 100644 (file)
@@ -1011,22 +1011,24 @@ nfs_delegation_find_inode_server(struct nfs_server *server,
                                 const struct nfs_fh *fhandle)
 {
        struct nfs_delegation *delegation;
-       struct inode *freeme, *res = NULL;
+       struct super_block *freeme = NULL;
+       struct inode *res = NULL;
 
        list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                spin_lock(&delegation->lock);
                if (delegation->inode != NULL &&
                    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
                    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
-                       freeme = igrab(delegation->inode);
-                       if (freeme && nfs_sb_active(freeme->i_sb))
-                               res = freeme;
+                       if (nfs_sb_active(server->super)) {
+                               freeme = server->super;
+                               res = igrab(delegation->inode);
+                       }
                        spin_unlock(&delegation->lock);
                        if (res != NULL)
                                return res;
                        if (freeme) {
                                rcu_read_unlock();
-                               iput(freeme);
+                               nfs_sb_deactive(freeme);
                                rcu_read_lock();
                        }
                        return ERR_PTR(-EAGAIN);