]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
staging: erofs: fix race of initializing xattrs of a inode at the same time
authorGao Xiang <gaoxiang25@huawei.com>
Mon, 18 Feb 2019 07:19:04 +0000 (15:19 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 20 Feb 2019 10:20:55 +0000 (11:20 +0100)
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/erofs/internal.h
drivers/staging/erofs/xattr.c

index 3a27c255950bd09adb36ff5e5dc8e3ddc4c39f97..e3bfde00c7d2030963204d3f62a10a798b03c964 100644 (file)
@@ -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 */
index 8c48b0ab31fd5b62ce5072d467498fde593c9a44..f716ab0446e5d31427f071ea98305a07b29e76aa 100644 (file)
@@ -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;
 }
 
 /*