linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] ubifs: Fixes for xattr concurrent operations
@ 2021-05-31 12:52 Zhihao Cheng
  2021-05-31 12:52 ` [PATCH 1/2] ubifs: Fix races between xattr_{set|get} and listxattr operations Zhihao Cheng
  2021-05-31 12:52 ` [PATCH 2/2] ubifs: Remove ui_mutex in ubifs_xattr_get and change_xattr Zhihao Cheng
  0 siblings, 2 replies; 4+ messages in thread
From: Zhihao Cheng @ 2021-05-31 12:52 UTC (permalink / raw)
  To: richard, yangds.fnst, s.hauer
  Cc: yi.zhang, houtao1, yukuai3, chengzhihao1, linux-mtd, linux-kernel

UBIFS may occur some problems with concurrent xattr_{set|get} and
listxattr operations, such as assertion failure, memory corruption,
stale xattr value[1].

Patch 1: Import a new rw-lock in @ubifs_inode to serilize write
	 operations on xattr
Patch 2: Remove unnecessary ui_mutex in ubifs_xattr_get and change_xattr

BTW changing @host->ui_mutex to a rw lock to adapting xattr operations
is a risky method, since @host->ui_mutex acts as too many roles:
* serializes inode write-back with the rest of VFS operations(such as
  rename, mkdir, rmdir, create, eg.)
* serializes "clean <-> dirty" state changes, serializes bulk-read
* protects @dirty, @bulk_read, @ui_size, and @xattr_size

Besides, if xattr operations and io/vfs operations share same rw-lock,
xattr operations may effect UBIFS performance more or less.

[1] https://lore.kernel.org/linux-mtd/20200630130438.141649-1-houtao1@huawei.com

Zhihao Cheng (2):
  ubifs: Fix races between xattr_{set|get} and listxattr operations
  ubifs: Remove ui_mutex in ubifs_xattr_get and change_xattr

 fs/ubifs/super.c |  1 +
 fs/ubifs/ubifs.h |  2 ++
 fs/ubifs/xattr.c | 48 +++++++++++++++++++++++++++++++++---------------
 3 files changed, 36 insertions(+), 15 deletions(-)

-- 
2.25.4


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

* [PATCH 1/2] ubifs: Fix races between xattr_{set|get} and listxattr operations
  2021-05-31 12:52 [PATCH 0/2] ubifs: Fixes for xattr concurrent operations Zhihao Cheng
@ 2021-05-31 12:52 ` Zhihao Cheng
  2021-06-16  8:23   ` Sascha Hauer
  2021-05-31 12:52 ` [PATCH 2/2] ubifs: Remove ui_mutex in ubifs_xattr_get and change_xattr Zhihao Cheng
  1 sibling, 1 reply; 4+ messages in thread
From: Zhihao Cheng @ 2021-05-31 12:52 UTC (permalink / raw)
  To: richard, yangds.fnst, s.hauer
  Cc: yi.zhang, houtao1, yukuai3, chengzhihao1, linux-mtd, linux-kernel

UBIFS may occur some problems with concurrent xattr_{set|get} and
listxattr operations, such as assertion failure, memory corruption,
stale xattr value[1].

Fix it by importing a new rw-lock in @ubifs_inode to serilize write
operations on xattr, concurrent read operations are still effective,
just like ext4.

[1] https://lore.kernel.org/linux-mtd/20200630130438.141649-1-houtao1@huawei.com

Fixes: 1e51764a3c2ac05a23 ("UBIFS: add new flash file system")
Cc: stable@vger.kernel.org  # v2.6+
Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
---
 fs/ubifs/super.c |  1 +
 fs/ubifs/ubifs.h |  2 ++
 fs/ubifs/xattr.c | 44 +++++++++++++++++++++++++++++++++-----------
 3 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index f65f665c5e12..94cf4f4675fe 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -275,6 +275,7 @@ static struct inode *ubifs_alloc_inode(struct super_block *sb)
 	memset((void *)ui + sizeof(struct inode), 0,
 	       sizeof(struct ubifs_inode) - sizeof(struct inode));
 	mutex_init(&ui->ui_mutex);
+	init_rwsem(&ui->xattr_sem);
 	spin_lock_init(&ui->ui_lock);
 	return &ui->vfs_inode;
 };
diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h
index b65c599a386a..7e978f421430 100644
--- a/fs/ubifs/ubifs.h
+++ b/fs/ubifs/ubifs.h
@@ -356,6 +356,7 @@ struct ubifs_gced_idx_leb {
  * @ui_mutex: serializes inode write-back with the rest of VFS operations,
  *            serializes "clean <-> dirty" state changes, serializes bulk-read,
  *            protects @dirty, @bulk_read, @ui_size, and @xattr_size
+ * @xattr_sem: serilizes write operations (remove|set|create) on xattr
  * @ui_lock: protects @synced_i_size
  * @synced_i_size: synchronized size of inode, i.e. the value of inode size
  *                 currently stored on the flash; used only for regular file
@@ -409,6 +410,7 @@ struct ubifs_inode {
 	unsigned int bulk_read:1;
 	unsigned int compr_type:2;
 	struct mutex ui_mutex;
+	struct rw_semaphore xattr_sem;
 	spinlock_t ui_lock;
 	loff_t synced_i_size;
 	loff_t ui_size;
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index 6b1e9830b274..1fce27e9b769 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -285,6 +285,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
 	if (!xent)
 		return -ENOMEM;
 
+	down_write(&ubifs_inode(host)->xattr_sem);
 	/*
 	 * The extended attribute entries are stored in LNC, so multiple
 	 * look-ups do not involve reading the flash.
@@ -319,6 +320,7 @@ int ubifs_xattr_set(struct inode *host, const char *name, const void *value,
 	iput(inode);
 
 out_free:
+	up_write(&ubifs_inode(host)->xattr_sem);
 	kfree(xent);
 	return err;
 }
@@ -341,18 +343,19 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
 	if (!xent)
 		return -ENOMEM;
 
+	down_read(&ubifs_inode(host)->xattr_sem);
 	xent_key_init(c, &key, host->i_ino, &nm);
 	err = ubifs_tnc_lookup_nm(c, &key, xent, &nm);
 	if (err) {
 		if (err == -ENOENT)
 			err = -ENODATA;
-		goto out_unlock;
+		goto out_cleanup;
 	}
 
 	inode = iget_xattr(c, le64_to_cpu(xent->inum));
 	if (IS_ERR(inode)) {
 		err = PTR_ERR(inode);
-		goto out_unlock;
+		goto out_cleanup;
 	}
 
 	ui = ubifs_inode(inode);
@@ -374,7 +377,8 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
 out_iput:
 	mutex_unlock(&ui->ui_mutex);
 	iput(inode);
-out_unlock:
+out_cleanup:
+	up_read(&ubifs_inode(host)->xattr_sem);
 	kfree(xent);
 	return err;
 }
@@ -406,16 +410,21 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
 	dbg_gen("ino %lu ('%pd'), buffer size %zd", host->i_ino,
 		dentry, size);
 
+	down_read(&host_ui->xattr_sem);
 	len = host_ui->xattr_names + host_ui->xattr_cnt;
-	if (!buffer)
+	if (!buffer) {
 		/*
 		 * We should return the minimum buffer size which will fit a
 		 * null-terminated list of all the extended attribute names.
 		 */
-		return len;
+		err = len;
+		goto out_err;
+	}
 
-	if (len > size)
-		return -ERANGE;
+	if (len > size) {
+		err = -ERANGE;
+		goto out_err;
+	}
 
 	lowest_xent_key(c, &key, host->i_ino);
 	while (1) {
@@ -437,8 +446,9 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
 		pxent = xent;
 		key_read(c, &xent->key, &key);
 	}
-
 	kfree(pxent);
+	up_read(&host_ui->xattr_sem);
+
 	if (err != -ENOENT) {
 		ubifs_err(c, "cannot find next direntry, error %d", err);
 		return err;
@@ -446,6 +456,10 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size)
 
 	ubifs_assert(c, written <= size);
 	return written;
+
+out_err:
+	up_read(&host_ui->xattr_sem);
+	return err;
 }
 
 static int remove_xattr(struct ubifs_info *c, struct inode *host,
@@ -504,6 +518,7 @@ int ubifs_purge_xattrs(struct inode *host)
 	ubifs_warn(c, "inode %lu has too many xattrs, doing a non-atomic deletion",
 		   host->i_ino);
 
+	down_write(&ubifs_inode(host)->xattr_sem);
 	lowest_xent_key(c, &key, host->i_ino);
 	while (1) {
 		xent = ubifs_tnc_next_ent(c, &key, &nm);
@@ -523,7 +538,7 @@ int ubifs_purge_xattrs(struct inode *host)
 			ubifs_ro_mode(c, err);
 			kfree(pxent);
 			kfree(xent);
-			return err;
+			goto out_err;
 		}
 
 		ubifs_assert(c, ubifs_inode(xino)->xattr);
@@ -535,7 +550,7 @@ int ubifs_purge_xattrs(struct inode *host)
 			kfree(xent);
 			iput(xino);
 			ubifs_err(c, "cannot remove xattr, error %d", err);
-			return err;
+			goto out_err;
 		}
 
 		iput(xino);
@@ -544,14 +559,19 @@ int ubifs_purge_xattrs(struct inode *host)
 		pxent = xent;
 		key_read(c, &xent->key, &key);
 	}
-
 	kfree(pxent);
+	up_write(&ubifs_inode(host)->xattr_sem);
+
 	if (err != -ENOENT) {
 		ubifs_err(c, "cannot find next direntry, error %d", err);
 		return err;
 	}
 
 	return 0;
+
+out_err:
+	up_write(&ubifs_inode(host)->xattr_sem);
+	return err;
 }
 
 /**
@@ -594,6 +614,7 @@ static int ubifs_xattr_remove(struct inode *host, const char *name)
 	if (!xent)
 		return -ENOMEM;
 
+	down_write(&ubifs_inode(host)->xattr_sem);
 	xent_key_init(c, &key, host->i_ino, &nm);
 	err = ubifs_tnc_lookup_nm(c, &key, xent, &nm);
 	if (err) {
@@ -618,6 +639,7 @@ static int ubifs_xattr_remove(struct inode *host, const char *name)
 	iput(inode);
 
 out_free:
+	up_write(&ubifs_inode(host)->xattr_sem);
 	kfree(xent);
 	return err;
 }
-- 
2.25.4


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

* [PATCH 2/2] ubifs: Remove ui_mutex in ubifs_xattr_get and change_xattr
  2021-05-31 12:52 [PATCH 0/2] ubifs: Fixes for xattr concurrent operations Zhihao Cheng
  2021-05-31 12:52 ` [PATCH 1/2] ubifs: Fix races between xattr_{set|get} and listxattr operations Zhihao Cheng
@ 2021-05-31 12:52 ` Zhihao Cheng
  1 sibling, 0 replies; 4+ messages in thread
From: Zhihao Cheng @ 2021-05-31 12:52 UTC (permalink / raw)
  To: richard, yangds.fnst, s.hauer
  Cc: yi.zhang, houtao1, yukuai3, chengzhihao1, linux-mtd, linux-kernel

Since ubifs_xattr_get and ubifs_xattr_set cannot being executed
parallelly after importing @host_ui->xattr_sem, now we can remove
ui_mutex imported by commit ab92a20bce3b4c2 ("ubifs: make
ubifs_[get|set]xattr atomic").

@xattr_size, @xattr_names and @xattr_cnt can't be out of protection
by @host_ui->mutex yet, they are sill accesed in other places, such as
pack_inode() called by ubifs_write_inode() triggered by page-writeback.

Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com>
---
 fs/ubifs/xattr.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index 1fce27e9b769..e4f193eae4b2 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -208,13 +208,11 @@ static int change_xattr(struct ubifs_info *c, struct inode *host,
 		err = -ENOMEM;
 		goto out_free;
 	}
-	mutex_lock(&ui->ui_mutex);
 	kfree(ui->data);
 	ui->data = buf;
 	inode->i_size = ui->ui_size = size;
 	old_size = ui->data_len;
 	ui->data_len = size;
-	mutex_unlock(&ui->ui_mutex);
 
 	mutex_lock(&host_ui->ui_mutex);
 	host->i_ctime = current_time(host);
@@ -362,7 +360,6 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
 	ubifs_assert(c, inode->i_size == ui->data_len);
 	ubifs_assert(c, ubifs_inode(host)->xattr_size > ui->data_len);
 
-	mutex_lock(&ui->ui_mutex);
 	if (buf) {
 		/* If @buf is %NULL we are supposed to return the length */
 		if (ui->data_len > size) {
@@ -375,7 +372,6 @@ ssize_t ubifs_xattr_get(struct inode *host, const char *name, void *buf,
 	err = ui->data_len;
 
 out_iput:
-	mutex_unlock(&ui->ui_mutex);
 	iput(inode);
 out_cleanup:
 	up_read(&ubifs_inode(host)->xattr_sem);
-- 
2.25.4


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

* Re: [PATCH 1/2] ubifs: Fix races between xattr_{set|get} and listxattr operations
  2021-05-31 12:52 ` [PATCH 1/2] ubifs: Fix races between xattr_{set|get} and listxattr operations Zhihao Cheng
@ 2021-06-16  8:23   ` Sascha Hauer
  0 siblings, 0 replies; 4+ messages in thread
From: Sascha Hauer @ 2021-06-16  8:23 UTC (permalink / raw)
  To: Zhihao Cheng
  Cc: richard, yangds.fnst, yi.zhang, houtao1, yukuai3, linux-mtd,
	linux-kernel

Hi,

On Mon, May 31, 2021 at 08:52:09PM +0800, Zhihao Cheng wrote:
> UBIFS may occur some problems with concurrent xattr_{set|get} and
> listxattr operations, such as assertion failure, memory corruption,
> stale xattr value[1].
> 
> Fix it by importing a new rw-lock in @ubifs_inode to serilize write
> operations on xattr, concurrent read operations are still effective,
> just like ext4.
> 
> [1] https://lore.kernel.org/linux-mtd/20200630130438.141649-1-houtao1@huawei.com

I had a closer look at it and reviewed that you carefully release the
lock where you claimed it. I can't tell if adding this lock is the right
approach, but for what it's worth you can add my

Reviewed-by: Sascha Hauer <s.hauer@pengutronix.de>

> --- a/fs/ubifs/ubifs.h
> +++ b/fs/ubifs/ubifs.h
> @@ -356,6 +356,7 @@ struct ubifs_gced_idx_leb {
>   * @ui_mutex: serializes inode write-back with the rest of VFS operations,
>   *            serializes "clean <-> dirty" state changes, serializes bulk-read,
>   *            protects @dirty, @bulk_read, @ui_size, and @xattr_size
> + * @xattr_sem: serilizes write operations (remove|set|create) on xattr

s/serilizes/serializes/

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

end of thread, other threads:[~2021-06-16  8:23 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-31 12:52 [PATCH 0/2] ubifs: Fixes for xattr concurrent operations Zhihao Cheng
2021-05-31 12:52 ` [PATCH 1/2] ubifs: Fix races between xattr_{set|get} and listxattr operations Zhihao Cheng
2021-06-16  8:23   ` Sascha Hauer
2021-05-31 12:52 ` [PATCH 2/2] ubifs: Remove ui_mutex in ubifs_xattr_get and change_xattr Zhihao Cheng

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).