mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	NFSv4: Fix a pNFS layout related use-after-free race when freeing the inode
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>
This commit is contained in:
		
							parent
							
								
									17068466ad
								
							
						
					
					
						commit
						b6d49ecd10
					
				
					 3 changed files with 37 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue