stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
@ 2019-02-14 15:34 Gao Xiang
  2019-02-15  7:29 ` Chao Yu
  2019-02-18  7:19 ` Gao Xiang
  0 siblings, 2 replies; 6+ messages in thread
From: Gao Xiang @ 2019-02-14 15:34 UTC (permalink / raw)
  To: linux-erofs, Chao Yu
  Cc: Chao Yu, Miao Xie, weidu.du, Fang Wei, Gao Xiang, stable

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.

That's actually an unexpected behavior, this patch closes the race.

Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
Cc: <stable@vger.kernel.org> # 4.19+
Signed-off-by: Gao Xiang <gaoxiang25@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);
+
+out_unlock:
+	clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
+	return ret;
 }
 
 /*
-- 
2.14.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
  2019-02-14 15:34 [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time Gao Xiang
@ 2019-02-15  7:29 ` Chao Yu
  2019-02-15  9:09   ` Gao Xiang
  2019-02-18  7:19 ` Gao Xiang
  1 sibling, 1 reply; 6+ messages in thread
From: Chao Yu @ 2019-02-15  7:29 UTC (permalink / raw)
  To: Gao Xiang, linux-erofs, Chao Yu; +Cc: Miao Xie, weidu.du, Fang Wei, stable

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@vger.kernel.org> # 4.19+
> Signed-off-by: Gao Xiang <gaoxiang25@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;
>  }
>  
>  /*
> 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
  2019-02-15  7:29 ` Chao Yu
@ 2019-02-15  9:09   ` Gao Xiang
  2019-02-18  1:43     ` Chao Yu
  0 siblings, 1 reply; 6+ messages in thread
From: Gao Xiang @ 2019-02-15  9:09 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei, stable

Hi Chao,

On 2019/2/15 15:29, Chao Yu wrote:
> 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@vger.kernel.org> # 4.19+
>> Signed-off-by: Gao Xiang <gaoxiang25@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;

set_bit(EROFS_V_EA_INITED_BIT, &vi->flags);    <- set xattrs initialized
clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);  <- clear EROFS_V_BL_XATTR_BIT bitlock and wake up waiting tasks...

Thanks,
Gao Xiang

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
  2019-02-15  9:09   ` Gao Xiang
@ 2019-02-18  1:43     ` Chao Yu
  2019-02-18  2:23       ` Gao Xiang
  0 siblings, 1 reply; 6+ messages in thread
From: Chao Yu @ 2019-02-18  1:43 UTC (permalink / raw)
  To: Gao Xiang; +Cc: linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei, stable

On 2019/2/15 17:09, Gao Xiang wrote:
> Hi Chao,
> 
> On 2019/2/15 15:29, Chao Yu wrote:
>> 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@vger.kernel.org> # 4.19+
>>> Signed-off-by: Gao Xiang <gaoxiang25@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;
> 
> set_bit(EROFS_V_EA_INITED_BIT, &vi->flags);    <- set xattrs initialized
> clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);  <- clear EROFS_V_BL_XATTR_BIT bitlock and wake up waiting tasks...

Yes, you're right, I just missed some points...

Reviewed-by: Chao Yu <yuchao0@huawei.com>

Thanks,

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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
  2019-02-18  1:43     ` Chao Yu
@ 2019-02-18  2:23       ` Gao Xiang
  0 siblings, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2019-02-18  2:23 UTC (permalink / raw)
  To: Chao Yu; +Cc: linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei, stable

Hi Chao,

On 2019/2/18 9:43, Chao Yu wrote:
> Yes, you're right, I just missed some points...
> 
> Reviewed-by: Chao Yu <yuchao0@huawei.com>
> 

Thanks for kindly reviewing, I can send this patch to the staging list later. :)

Thanks,
Gao Xiang

> Thanks,

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time
  2019-02-14 15:34 [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time Gao Xiang
  2019-02-15  7:29 ` Chao Yu
@ 2019-02-18  7:19 ` Gao Xiang
  1 sibling, 0 replies; 6+ messages in thread
From: Gao Xiang @ 2019-02-18  7:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Chao Yu, devel
  Cc: LKML, linux-erofs, Chao Yu, Miao Xie, weidu.du, Fang Wei,
	Gao Xiang, stable

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.

That's actually an unexpected behavior, this patch closes the race.

Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support")
Cc: <stable@vger.kernel.org> # 4.19+
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
---
[previewed at https://lists.ozlabs.org/pipermail/linux-erofs/2019-February/001345.html]

 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..f716ab0446e5 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 (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 (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);
+
+out_unlock:
+	clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
+	return ret;
 }
 
 /*
-- 
2.14.4


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-02-18  7:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-02-14 15:34 [PREVIEW] [PATCH] staging: erofs: fix race of initializing xattrs of a inode at the same time Gao Xiang
2019-02-15  7:29 ` Chao Yu
2019-02-15  9:09   ` Gao Xiang
2019-02-18  1:43     ` Chao Yu
2019-02-18  2:23       ` Gao Xiang
2019-02-18  7:19 ` Gao Xiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).