]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
NFSv4: Fix a pNFS layout related use-after-free race when freeing the inode
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 25 Nov 2020 17:06:14 +0000 (12:06 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Wed, 2 Dec 2020 19:05:54 +0000 (14:05 -0500)
When returning the layout in nfs4_evict_inode(), we need to ensure that
the layout is actually done being freed before we can proceed to free the
inode itself.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/nfs4super.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h

index 93f5c1678ec2912fd156c4ee35c79386616e5b04..984cc42ee54d8ccd93e3ba8a0afa9eb85e0c2042 100644 (file)
@@ -67,7 +67,7 @@ static void nfs4_evict_inode(struct inode *inode)
        nfs_inode_evict_delegation(inode);
        /* Note that above delegreturn would trigger pnfs return-on-close */
        pnfs_return_layout(inode);
-       pnfs_destroy_layout(NFS_I(inode));
+       pnfs_destroy_layout_final(NFS_I(inode));
        /* First call standard NFS clear_inode() code */
        nfs_clear_inode(inode);
        nfs4_xattr_cache_zap(inode);
index 0e50b9d45c32095d21080350d64991d56c895acd..07f59dc8cb2e77e17c8d7cbcbbac94a58c9b9256 100644 (file)
@@ -294,6 +294,7 @@ void
 pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo)
 {
        struct inode *inode;
+       unsigned long i_state;
 
        if (!lo)
                return;
@@ -304,8 +305,12 @@ pnfs_put_layout_hdr(struct pnfs_layout_hdr *lo)
                if (!list_empty(&lo->plh_segs))
                        WARN_ONCE(1, "NFS: BUG unfreed layout segments.\n");
                pnfs_detach_layout_hdr(lo);
+               i_state = inode->i_state;
                spin_unlock(&inode->i_lock);
                pnfs_free_layout_hdr(lo);
+               /* Notify pnfs_destroy_layout_final() that we're done */
+               if (i_state & (I_FREEING | I_CLEAR))
+                       wake_up_var(lo);
        }
 }
 
@@ -734,8 +739,7 @@ pnfs_free_lseg_list(struct list_head *free_me)
        }
 }
 
-void
-pnfs_destroy_layout(struct nfs_inode *nfsi)
+static struct pnfs_layout_hdr *__pnfs_destroy_layout(struct nfs_inode *nfsi)
 {
        struct pnfs_layout_hdr *lo;
        LIST_HEAD(tmp_list);
@@ -753,9 +757,34 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
                pnfs_put_layout_hdr(lo);
        } else
                spin_unlock(&nfsi->vfs_inode.i_lock);
+       return lo;
+}
+
+void pnfs_destroy_layout(struct nfs_inode *nfsi)
+{
+       __pnfs_destroy_layout(nfsi);
 }
 EXPORT_SYMBOL_GPL(pnfs_destroy_layout);
 
+static bool pnfs_layout_removed(struct nfs_inode *nfsi,
+                               struct pnfs_layout_hdr *lo)
+{
+       bool ret;
+
+       spin_lock(&nfsi->vfs_inode.i_lock);
+       ret = nfsi->layout != lo;
+       spin_unlock(&nfsi->vfs_inode.i_lock);
+       return ret;
+}
+
+void pnfs_destroy_layout_final(struct nfs_inode *nfsi)
+{
+       struct pnfs_layout_hdr *lo = __pnfs_destroy_layout(nfsi);
+
+       if (lo)
+               wait_var_event(lo, pnfs_layout_removed(nfsi, lo));
+}
+
 static bool
 pnfs_layout_add_bulk_destroy_list(struct inode *inode,
                struct list_head *layout_list)
index f618c49697bbaadb9ccf9c2faeaaff002bba5614..bbd3de1025f23cb3bc5e78a46abef5e1ae56eba5 100644 (file)
@@ -268,6 +268,7 @@ struct pnfs_layout_segment *pnfs_layout_process(struct nfs4_layoutget *lgp);
 void pnfs_layoutget_free(struct nfs4_layoutget *lgp);
 void pnfs_free_lseg_list(struct list_head *tmp_list);
 void pnfs_destroy_layout(struct nfs_inode *);
+void pnfs_destroy_layout_final(struct nfs_inode *);
 void pnfs_destroy_all_layouts(struct nfs_client *);
 int pnfs_destroy_layouts_byfsid(struct nfs_client *clp,
                struct nfs_fsid *fsid,
@@ -712,6 +713,10 @@ static inline void pnfs_destroy_layout(struct nfs_inode *nfsi)
 {
 }
 
+static inline void pnfs_destroy_layout_final(struct nfs_inode *nfsi)
+{
+}
+
 static inline struct pnfs_layout_segment *
 pnfs_get_lseg(struct pnfs_layout_segment *lseg)
 {