diff options
author | Eryu Guan <guaneryu@gmail.com> | 2018-03-22 11:41:25 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2018-03-22 11:41:25 -0400 |
commit | 73fdad00b208b139cf43f3163fbc0f67e4c6047c (patch) | |
tree | df87fefb83a3a4a7a2416dcd08b6ce3c717d7f5e | |
parent | 044e6e3d74a3d7103a0c8a9305dfd94d64000660 (diff) |
ext4: protect i_disksize update by i_data_sem in direct write path
i_disksize update should be protected by i_data_sem, by either taking
the lock explicitly or by using ext4_update_i_disksize() helper. But the
i_disksize updates in ext4_direct_IO_write() are not protected at all,
which may be racing with i_disksize updates in writeback path in
delalloc buffer write path.
This is found by code inspection, and I didn't hit any i_disksize
corruption due to this bug. Thanks to Jan Kara for catching this bug and
suggesting the fix!
Reported-by: Jan Kara <jack@suse.cz>
Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
-rw-r--r-- | fs/ext4/inode.c | 5 |
1 files changed, 2 insertions, 3 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c94780075b04..bff44b4a0783 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -3658,7 +3658,6 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter) | |||
3658 | { | 3658 | { |
3659 | struct file *file = iocb->ki_filp; | 3659 | struct file *file = iocb->ki_filp; |
3660 | struct inode *inode = file->f_mapping->host; | 3660 | struct inode *inode = file->f_mapping->host; |
3661 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
3662 | ssize_t ret; | 3661 | ssize_t ret; |
3663 | loff_t offset = iocb->ki_pos; | 3662 | loff_t offset = iocb->ki_pos; |
3664 | size_t count = iov_iter_count(iter); | 3663 | size_t count = iov_iter_count(iter); |
@@ -3682,7 +3681,7 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter) | |||
3682 | goto out; | 3681 | goto out; |
3683 | } | 3682 | } |
3684 | orphan = 1; | 3683 | orphan = 1; |
3685 | ei->i_disksize = inode->i_size; | 3684 | ext4_update_i_disksize(inode, inode->i_size); |
3686 | ext4_journal_stop(handle); | 3685 | ext4_journal_stop(handle); |
3687 | } | 3686 | } |
3688 | 3687 | ||
@@ -3790,7 +3789,7 @@ static ssize_t ext4_direct_IO_write(struct kiocb *iocb, struct iov_iter *iter) | |||
3790 | if (ret > 0) { | 3789 | if (ret > 0) { |
3791 | loff_t end = offset + ret; | 3790 | loff_t end = offset + ret; |
3792 | if (end > inode->i_size) { | 3791 | if (end > inode->i_size) { |
3793 | ei->i_disksize = end; | 3792 | ext4_update_i_disksize(inode, end); |
3794 | i_size_write(inode, end); | 3793 | i_size_write(inode, end); |
3795 | /* | 3794 | /* |
3796 | * We're going to return a positive `ret' | 3795 | * We're going to return a positive `ret' |