[PATCH] ext4: fix data-races problem at inode->i_disksize

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We find some data-race problems at inode->i_disksize by
using KSCAN in kernel 4.18. The same problem can also be
found at commit dce8e237100f ("ext4: fix a data race at
inode->i_disksize").

BUG: KCSAN: data-race in ext4_da_write_end / ext4_writepages
write to 0xffff8ee8ed62cea8 of 8 bytes by task 3908 on cpu 0:
mpage_map_and_submit_extent  [inline]
ext4_writepages+0x170c/0x1b90
do_writepages+0x70/0x170
__filemap_fdatawrite_range+0x199/0x1f0
file_write_and_wait_range+0x80/0xc0
ext4_sync_file+0x26f/0x860
vfs_fsync_range+0x7a/0x130
vfs_fsync  [inline]
do_fsync+0x4c/0x80
__do_sys_fsync  [inline]
__se_sys_fsync  [inline]
__x64_sys_fsync+0x2c/0x40
do_syscall_64+0xb5/0x340
entry_SYSCALL_64_after_hwframe+0x65/0xca
0xffffffffffffffff

read to 0xffff8ee8ed62cea8 of 8 bytes by task 3907 on cpu 3:
ext4_da_write_end+0xd3/0x7d0
generic_perform_write+0x1c6/0x2c0
__generic_file_write_iter+0x2aa/0x2f0
ext4_file_write_iter+0x197/0x820
call_write_iter   [inline]
new_sync_write+0x2ae/0x350
__vfs_write+0xa5/0xc0
vfs_write+0x119/0x2e0
ksys_write+0x83/0x120
__do_sys_write  [inline]
__se_sys_write  [inline]
__x64_sys_write+0x4d/0x60
do_syscall_64+0xb5/0x340
entry_SYSCALL_64_after_hwframe+0x65/0xca
0xffffffffffffffff

We find two solutions to solve this problem.
1) Just the same as commit dce8e237100f ("ext4: fix a data
race at inode->i_disksize"), Add READ_ONCE or WRITE_ONCE
on inode->i_disksize directly.

It is helpful to avoid current KCSAN problem. However,
some other code using inode->i_disksize without READ_ONCE
or WRITE_ONCE may also have KCSAN problem. So, we try to
use the second solution.

2) Add 'volatile' keyword at inode->i_disksize.

We think this solution may be helpful to deal with the
date-race problem on inode->i_disksize.

Reported-by: Wenhao Zhang <zhangwenhao8@xxxxxxxxxx>
Signed-off-by: Haotian Li <lihaotian9@xxxxxxxxxx>
---
 fs/ext4/ext4.h  | 4 ++--
 fs/ext4/inode.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 523e00d7b392..354a9d1371af 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1036,7 +1036,7 @@ struct ext4_inode_info {
 	 * a truncate is in progress.  The only things which change i_disksize
 	 * are ext4_get_block (growth) and ext4_truncate (shrinkth).
 	 */
-	loff_t	i_disksize;
+	volatile loff_t	i_disksize;

 	/*
 	 * i_data_sem is for serialising ext4_truncate() against
@@ -3128,7 +3128,7 @@ static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize)
 		     !inode_is_locked(inode));
 	down_write(&EXT4_I(inode)->i_data_sem);
 	if (newsize > EXT4_I(inode)->i_disksize)
-		WRITE_ONCE(EXT4_I(inode)->i_disksize, newsize);
+		EXT4_I(inode)->i_disksize = newsize;
 	up_write(&EXT4_I(inode)->i_data_sem);
 }

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index bf596467c234..7c89d07dead3 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2476,7 +2476,7 @@ static int mpage_map_and_submit_extent(handle_t *handle,
 	 * truncate are avoided by checking i_size under i_data_sem.
 	 */
 	disksize = ((loff_t)mpd->first_page) << PAGE_SHIFT;
-	if (disksize > READ_ONCE(EXT4_I(inode)->i_disksize)) {
+	if (disksize > EXT4_I(inode)->i_disksize) {
 		int err2;
 		loff_t i_size;

@@ -5015,7 +5015,7 @@ static int ext4_do_update_inode(handle_t *handle,
 		raw_inode->i_file_acl_high =
 			cpu_to_le16(ei->i_file_acl >> 32);
 	raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl);
-	if (READ_ONCE(ei->i_disksize) != ext4_isize(inode->i_sb, raw_inode)) {
+	if (ei->i_disksize != ext4_isize(inode->i_sb, raw_inode)) {
 		ext4_isize_set(raw_inode, ei->i_disksize);
 		need_datasync = 1;
 	}
-- 
2.19.1




[Index of Archives]     [Reiser Filesystem Development]     [Ceph FS]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite National Park]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Media]

  Powered by Linux