NFSv4: Fix a pNFS layout related use-after-free race when freeing the inode
[ Upstream commit b6d49ecd1081740b6e632366428b960461f8158b ] 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> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
d52faa7fb1
commit
dbe184f6be
@ -96,7 +96,7 @@ static void nfs4_evict_inode(struct inode *inode)
|
||||
nfs_inode_return_delegation_noreclaim(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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -723,8 +728,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);
|
||||
@ -742,9 +746,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)
|
||||
|
@ -255,6 +255,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,
|
||||
@ -651,6 +652,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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user