2022-03-16 01:12:54

by Tadeusz Struk

[permalink] [raw]
Subject: [PATCH] ext4: check if offset+length is within a valid range in fallocate

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 disk size,
and trigger a BUG in ext4_ind_remove_space() [3].
According to the comment doc in ext4_ind_remove_space() the 'end'
parameter needs to be one block after the last block to remove.
In the case when the BUG is triggered it points to the last block on
a 4GB virtual disk image. This is calculated in
ext4_ind_remove_space() in [4].
This patch adds a check that ensure the length + offest to be
within the valid range and returns -ENOSPC error code in case
it is invalid.

LINK: [1] https://syzkaller.appspot.com/bug?id=b80bd9cf348aac724a4f4dff251800106d721331
LINK: [2] https://syzkaller.appspot.com/text?tag=ReproC&x=14ba0238700000
LINK: [3] https://elixir.bootlin.com/linux/v5.17-rc8/source/fs/ext4/indirect.c#L1244
LINK: [4] https://elixir.bootlin.com/linux/v5.17-rc8/source/fs/ext4/indirect.c#L1234

Cc: Theodore Ts'o <[email protected]>
Cc: Andreas Dilger <[email protected]>
Cc: Ritesh Harjani <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>

Fixes: a4bb6b64e39a ("ext4: enable "punch hole" functionality")
Reported-by: [email protected]
Signed-off-by: Tadeusz Struk <[email protected]>
---
fs/ext4/inode.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 01c9e4f743ba..dd9c35113efe 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3924,7 +3924,8 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
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, ret2 = 0;
@@ -3967,6 +3968,16 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
offset;
}

+ /*
+ * For punch hole the length + offset needs to be at least within
+ * one block before last
+ */
+ max_length = sbi->s_bitmap_maxbytes - sbi->s_blocksize;
+ if (offset + length >= max_length) {
+ ret = -ENOSPC;
+ goto out_mutex;
+ }
+
if (offset & (sb->s_blocksize - 1) ||
(offset + length) & (sb->s_blocksize - 1)) {
/*
--
2.35.1


2022-03-16 12:31:28

by Tadeusz Struk

[permalink] [raw]
Subject: Re: [PATCH] ext4: check if offset+length is within a valid range in fallocate

On 3/15/22 18:22, kernel test robot wrote:
> Hi Tadeusz,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on tytso-ext4/dev]
> [also build test ERROR on linus/master v5.17-rc8 next-20220315]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch]
>
> url:https://github.com/0day-ci/linux/commits/Tadeusz-Struk/ext4-check-if-offset-length-is-within-a-valid-range-in-fallocate/20220316-031841
> base:https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
> config: arc-randconfig-r043-20220313 (https://download.01.org/0day-ci/archive/20220316/[email protected]/config)
> compiler: arc-elf-gcc (GCC) 11.2.0
> reproduce (this is a W=1 build):
> wgethttps://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> #https://github.com/0day-ci/linux/commit/bc1fdc20f07523e970c9dea4f0fbabbc437fb0d5
> git remote add linux-reviewhttps://github.com/0day-ci/linux
> git fetch --no-tags linux-review Tadeusz-Struk/ext4-check-if-offset-length-is-within-a-valid-range-in-fallocate/20220316-031841
> git checkout bc1fdc20f07523e970c9dea4f0fbabbc437fb0d5
> # save the config file to linux build tree
> mkdir build_dir
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash fs/
>
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot<[email protected]>
>
> All errors (new ones prefixed by >>):
>
> fs/ext4/inode.c: In function 'ext4_punch_hole':
>>> fs/ext4/inode.c:4002:50: error: 'struct ext4_sb_info' has no member named 's_blocksize'
> 4002 | max_length = sbi->s_bitmap_maxbytes - sbi->s_blocksize;
> | ^~

Thanks for report, but I've already sent v2:
https://lore.kernel.org/linux-ext4/[email protected]

--
Thanks,
Tadeusz

2022-03-16 19:25:42

by Tadeusz Struk

[permalink] [raw]
Subject: Re: [PATCH] ext4: check if offset+length is within a valid range in fallocate

On 3/15/22 12:15, Tadeusz Struk wrote:
> @@ -3967,6 +3968,16 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
> offset;
> }
>
> + /*
> + * For punch hole the length + offset needs to be at least within
> + * one block before last
> + */
> + max_length = sbi->s_bitmap_maxbytes - sbi->s_blocksize;

that supposed to be:
max_length = sbi->s_bitmap_maxbytes - inode->i_sb->s_blocksize;

Please ignore this one. I will send a new version soon.
Sorry for the noise.

> + if (offset + length >= max_length) {
> + ret = -ENOSPC;
> + goto out_mutex;
> + }
> +


--
Thanks,
Tadeusz