[PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time

Chao Yu yuchao0 at huawei.com
Fri Feb 15 18:29:33 AEDT 2019


On 2019/2/14 23:34, Gao Xiang wrote:
> In real scenario, there could be several threads accessing xattrs
> of the same xattr-uninitialized inode, and init_inode_xattrs()
> almost at the same time.

Yeah, nice catch!

> 
> That's actually an unexpected behavior, this patch closes the race.
> 
> Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
> Cc: <stable at vger.kernel.org> # 4.19+
> Signed-off-by: Gao Xiang <gaoxiang25 at huawei.com>
> ---
>  drivers/staging/erofs/internal.h | 11 ++++++++---
>  drivers/staging/erofs/xattr.c    | 41 ++++++++++++++++++++++++++++------------
>  2 files changed, 37 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/staging/erofs/internal.h b/drivers/staging/erofs/internal.h
> index 3a27c255950b..e3bfde00c7d2 100644
> --- a/drivers/staging/erofs/internal.h
> +++ b/drivers/staging/erofs/internal.h
> @@ -327,12 +327,17 @@ static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid)
>  	return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits);
>  }
>  
> -#define inode_set_inited_xattr(inode)   (EROFS_V(inode)->flags |= 1)
> -#define inode_has_inited_xattr(inode)   (EROFS_V(inode)->flags & 1)
> +/* atomic flag definitions */
> +#define EROFS_V_EA_INITED_BIT	0
> +
> +/* bitlock definitions (arranged in reverse order) */
> +#define EROFS_V_BL_XATTR_BIT	(BITS_PER_LONG - 1)
>  
>  struct erofs_vnode {
>  	erofs_nid_t nid;
> -	unsigned int flags;
> +
> +	/* atomic flags (including bitlocks) */
> +	unsigned long flags;
>  
>  	unsigned char data_mapping_mode;
>  	/* inline size in bytes */
> diff --git a/drivers/staging/erofs/xattr.c b/drivers/staging/erofs/xattr.c
> index 8c48b0ab31fd..a188aad00bf8 100644
> --- a/drivers/staging/erofs/xattr.c
> +++ b/drivers/staging/erofs/xattr.c
> @@ -44,18 +44,25 @@ static inline void xattr_iter_end_final(struct xattr_iter *it)
>  
>  static int init_inode_xattrs(struct inode *inode)
>  {
> +	struct erofs_vnode *const vi = EROFS_V(inode);
>  	struct xattr_iter it;
>  	unsigned int i;
>  	struct erofs_xattr_ibody_header *ih;
>  	struct super_block *sb;
>  	struct erofs_sb_info *sbi;
> -	struct erofs_vnode *vi;
>  	bool atomic_map;
> +	int ret = 0;
>  
> -	if (likely(inode_has_inited_xattr(inode)))
> +	/* the most case is that xattrs of this inode are initialized. */
> +	if (likely(test_bit(EROFS_V_EA_INITED_BIT, &vi->flags)))
>  		return 0;
>  
> -	vi = EROFS_V(inode);
> +	if (wait_on_bit_lock(&vi->flags, EROFS_V_BL_XATTR_BIT, TASK_KILLABLE))
> +		return -ERESTARTSYS;
> +
> +	/* someone has initialized xattrs for us? */
> +	if (unlikely(test_bit(EROFS_V_EA_INITED_BIT, &vi->flags)))
> +		goto out_unlock;
>  
>  	/*
>  	 * bypass all xattr operations if ->xattr_isize is not greater than
> @@ -68,13 +75,16 @@ static int init_inode_xattrs(struct inode *inode)
>  	if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) {
>  		errln("xattr_isize %d of nid %llu is not supported yet",
>  		      vi->xattr_isize, vi->nid);
> -		return -ENOTSUPP;
> +		ret = -ENOTSUPP;
> +		goto out_unlock;
>  	} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) {
>  		if (unlikely(vi->xattr_isize)) {
>  			DBG_BUGON(1);
> -			return -EIO;	/* xattr ondisk layout error */
> +			ret = -EIO;
> +			goto out_unlock;	/* xattr ondisk layout error */
>  		}
> -		return -ENOATTR;
> +		ret = -ENOATTR;
> +		goto out_unlock;
>  	}
>  
>  	sb = inode->i_sb;
> @@ -83,8 +93,10 @@ static int init_inode_xattrs(struct inode *inode)
>  	it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
>  
>  	it.page = erofs_get_inline_page(inode, it.blkaddr);
> -	if (IS_ERR(it.page))
> -		return PTR_ERR(it.page);
> +	if (IS_ERR(it.page)) {
> +		ret = PTR_ERR(it.page);
> +		goto out_unlock;
> +	}
>  
>  	/* read in shared xattr array (non-atomic, see kmalloc below) */
>  	it.kaddr = kmap(it.page);
> @@ -97,7 +109,8 @@ static int init_inode_xattrs(struct inode *inode)
>  						sizeof(uint), GFP_KERNEL);
>  	if (vi->xattr_shared_xattrs == NULL) {
>  		xattr_iter_end(&it, atomic_map);
> -		return -ENOMEM;
> +		ret = -ENOMEM;
> +		goto out_unlock;
>  	}
>  
>  	/* let's skip ibody header */
> @@ -114,7 +127,8 @@ static int init_inode_xattrs(struct inode *inode)
>  			if (IS_ERR(it.page)) {
>  				kfree(vi->xattr_shared_xattrs);
>  				vi->xattr_shared_xattrs = NULL;
> -				return PTR_ERR(it.page);
> +				ret = PTR_ERR(it.page);
> +				goto out_unlock;
>  			}
>  
>  			it.kaddr = kmap_atomic(it.page);
> @@ -127,8 +141,11 @@ static int init_inode_xattrs(struct inode *inode)
>  	}
>  	xattr_iter_end(&it, atomic_map);
>  
> -	inode_set_inited_xattr(inode);
> -	return 0;
> +	set_bit(EROFS_V_EA_INITED_BIT, &vi->flags);

Should it be?

set_bit();
wake_up_bit();
return 0;

> +
> +out_unlock:
> +	clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
> +	return ret;
>  }
>  
>  /*
> 



More information about the Linux-erofs mailing list