| From 2da376228a2427501feb9d15815a45dbdbdd753e Mon Sep 17 00:00:00 2001 |
| From: Tadeusz Struk <tadeusz.struk@linaro.org> |
| Date: Thu, 31 Mar 2022 13:05:15 -0700 |
| Subject: ext4: limit length to bitmap_maxbytes - blocksize in punch_hole |
| |
| From: Tadeusz Struk <tadeusz.struk@linaro.org> |
| |
| commit 2da376228a2427501feb9d15815a45dbdbdd753e upstream. |
| |
| 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: a4bb6b64e39a ("ext4: enable "punch hole" functionality") |
| Reported-by: syzbot+7a806094edd5d07ba029@syzkaller.appspotmail.com |
| Signed-off-by: Tadeusz Struk <tadeusz.struk@linaro.org> |
| Link: https://lore.kernel.org/r/20220331200515.153214-1-tadeusz.struk@linaro.org |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Cc: stable@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/inode.c | 11 ++++++++++- |
| 1 file changed, 10 insertions(+), 1 deletion(-) |
| |
| --- a/fs/ext4/inode.c |
| +++ b/fs/ext4/inode.c |
| @@ -4311,7 +4311,8 @@ int ext4_punch_hole(struct inode *inode, |
| struct super_block *sb = inode->i_sb; |
| ext4_lblk_t first_block, stop_block; |
| struct address_space *mapping = inode->i_mapping; |
| - loff_t first_block_offset, last_block_offset; |
| + loff_t first_block_offset, last_block_offset, max_length; |
| + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); |
| handle_t *handle; |
| unsigned int credits; |
| int ret = 0; |
| @@ -4357,6 +4358,14 @@ int ext4_punch_hole(struct inode *inode, |
| offset; |
| } |
| |
| + /* |
| + * For punch hole the length + offset needs to be within one block |
| + * before last range. Adjust the length if it goes beyond that limit. |
| + */ |
| + max_length = sbi->s_bitmap_maxbytes - inode->i_sb->s_blocksize; |
| + if (offset + length > max_length) |
| + length = max_length - offset; |
| + |
| if (offset & (sb->s_blocksize - 1) || |
| (offset + length) & (sb->s_blocksize - 1)) { |
| /* |