Skip to content

Commit 2da3762

Browse files
tstruktytso
authored andcommitted
ext4: limit length to bitmap_maxbytes - blocksize in punch_hole
Syzbot found an issue [1] in ext4_fallocate(). The C reproducer [2] calls fallocate(), passing size 0xffeffeff000ul, and offset 0x1000000ul, which, when added together exceed the bitmap_maxbytes for the inode. This triggers a BUG in ext4_ind_remove_space(). According to the comments in this function the 'end' parameter needs to be one block after the last block to be removed. In the case when the BUG is triggered it points to the last block. Modify the ext4_punch_hole() function and add constraint that caps the length to satisfy the one before laster block requirement. LINK: [1] https://syzkaller.appspot.com/bug?id=b80bd9cf348aac724a4f4dff251800106d721331 LINK: [2] https://syzkaller.appspot.com/text?tag=ReproC&x=14ba0238700000 Fixes: a4bb6b6 ("ext4: enable "punch hole" functionality") Reported-by: [email protected] Signed-off-by: Tadeusz Struk <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]> Cc: [email protected]
1 parent c186f08 commit 2da3762

File tree

1 file changed

+10
-1
lines changed

1 file changed

+10
-1
lines changed

fs/ext4/inode.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3952,7 +3952,8 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
39523952
struct super_block *sb = inode->i_sb;
39533953
ext4_lblk_t first_block, stop_block;
39543954
struct address_space *mapping = inode->i_mapping;
3955-
loff_t first_block_offset, last_block_offset;
3955+
loff_t first_block_offset, last_block_offset, max_length;
3956+
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
39563957
handle_t *handle;
39573958
unsigned int credits;
39583959
int ret = 0, ret2 = 0;
@@ -3995,6 +3996,14 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
39953996
offset;
39963997
}
39973998

3999+
/*
4000+
* For punch hole the length + offset needs to be within one block
4001+
* before last range. Adjust the length if it goes beyond that limit.
4002+
*/
4003+
max_length = sbi->s_bitmap_maxbytes - inode->i_sb->s_blocksize;
4004+
if (offset + length > max_length)
4005+
length = max_length - offset;
4006+
39984007
if (offset & (sb->s_blocksize - 1) ||
39994008
(offset + length) & (sb->s_blocksize - 1)) {
40004009
/*

0 commit comments

Comments
 (0)