diff options
author | Christoph Hellwig <hch@lst.de> | 2023-09-25 08:54:45 -0700 |
---|---|---|
committer | Darrick J. Wong <djwong@kernel.org> | 2023-09-25 08:55:00 -0700 |
commit | 381c043233e66b1c160ef235675e65cf6c580e92 (patch) | |
tree | ec44e4f53fd024c3dfa14343eca8de8b488eb5a3 | |
parent | a5f31a5028d1e88e97c3b6cdc3e3bf2da085e232 (diff) |
iomap: add a workaround for racy i_size updates on block devices
A szybot reproducer that does write I/O while truncating the size of a
block device can end up in clean_bdev_aliases, which tries to clean the
bdev aliases that it uses. This is because iomap_to_bh automatically
sets the BH_New flag when outside of i_size. For block devices updates
to i_size are racy and we can hit this case in a tiny race window,
leading to the eventual clean_bdev_aliases call. Fix this by erroring
out of > i_size I/O on block devices.
Reported-by: syzbot+1fa947e7f09e136925b8@syzkaller.appspotmail.com
Signed-off-by: Christoph Hellwig <hch@lst.de>
Tested-by: syzbot+1fa947e7f09e136925b8@syzkaller.appspotmail.com
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
-rw-r--r-- | fs/buffer.c | 11 |
1 files changed, 10 insertions, 1 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index a6785cd07081..12e9a71c693d 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2058,8 +2058,17 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, fallthrough; case IOMAP_MAPPED: if ((iomap->flags & IOMAP_F_NEW) || - offset >= i_size_read(inode)) + offset >= i_size_read(inode)) { + /* + * This can happen if truncating the block device races + * with the check in the caller as i_size updates on + * block devices aren't synchronized by i_rwsem for + * block devices. + */ + if (S_ISBLK(inode->i_mode)) + return -EIO; set_buffer_new(bh); + } bh->b_blocknr = (iomap->addr + offset - iomap->offset) >> inode->i_blkbits; set_buffer_mapped(bh); |