fs/ext4/migrate.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
update_tind_extent_range() advances lb->curr_block, an ext4_lblk_t, by
max_entries * max_entries for every empty triple-indirect slot. One
triple-indirect block spans max_entries^3 logical blocks, which exceeds
2^32 as soon as the block size is 8K or larger (16384^3 = 2^42 with 64K
blocks), so the counter wraps while that block is walked.
A wrapped counter makes the migration store a block number that is 2^32
blocks away from the one the pointer block describes. Two ranges can then
end up with the same ee_block, which trips
BUG_ON(newext->ee_block == nearex->ee_block) in ext4_ext_insert_extent(),
and without that collision the data is still moved to the wrong logical
block while the migration reports success.
Keep the counter in 64 bit so that it cannot wrap, and refuse the
migration with -EOPNOTSUPP when a data block is found after the last
logical block an extent can describe, which only a corrupt block map can
contain.
Fixes: c14c6fd5c56a ("ext4: Add EXT4_IOC_MIGRATE ioctl")
Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
---
fs/ext4/migrate.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
index e06d847033a1..8043959c19ef 100644
--- a/fs/ext4/migrate.c
+++ b/fs/ext4/migrate.c
@@ -14,7 +14,7 @@
* represented by a single extent
*/
struct migrate_struct {
- ext4_lblk_t first_block, last_block, curr_block;
+ u64 first_block, last_block, curr_block;
ext4_fsblk_t first_pblock, last_pblock;
};
@@ -65,6 +65,10 @@ static int update_extent_range(handle_t *handle, struct inode *inode,
ext4_fsblk_t pblock, struct migrate_struct *lb)
{
int retval;
+
+ if (lb->curr_block > (ext4_lblk_t)-1)
+ return -EOPNOTSUPP;
+
/*
* See if we can add on to the existing range (if it exists)
*/
--
2.51.0
On Mon 14-09-26 14:55:44, Yichong Chen wrote:
> update_tind_extent_range() advances lb->curr_block, an ext4_lblk_t, by
> max_entries * max_entries for every empty triple-indirect slot. One
> triple-indirect block spans max_entries^3 logical blocks, which exceeds
> 2^32 as soon as the block size is 8K or larger (16384^3 = 2^42 with 64K
> blocks), so the counter wraps while that block is walked.
>
> A wrapped counter makes the migration store a block number that is 2^32
> blocks away from the one the pointer block describes. Two ranges can then
> end up with the same ee_block, which trips
> BUG_ON(newext->ee_block == nearex->ee_block) in ext4_ext_insert_extent(),
> and without that collision the data is still moved to the wrong logical
> block while the migration reports success.
>
> Keep the counter in 64 bit so that it cannot wrap, and refuse the
> migration with -EOPNOTSUPP when a data block is found after the last
> logical block an extent can describe, which only a corrupt block map can
> contain.
>
> Fixes: c14c6fd5c56a ("ext4: Add EXT4_IOC_MIGRATE ioctl")
> Signed-off-by: Yichong Chen <chenyichong@uniontech.com>
Yeah, I guess this is the easiest way how to deal with this. Feel free to
add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/ext4/migrate.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c
> index e06d847033a1..8043959c19ef 100644
> --- a/fs/ext4/migrate.c
> +++ b/fs/ext4/migrate.c
> @@ -14,7 +14,7 @@
> * represented by a single extent
> */
> struct migrate_struct {
> - ext4_lblk_t first_block, last_block, curr_block;
> + u64 first_block, last_block, curr_block;
> ext4_fsblk_t first_pblock, last_pblock;
> };
>
> @@ -65,6 +65,10 @@ static int update_extent_range(handle_t *handle, struct inode *inode,
> ext4_fsblk_t pblock, struct migrate_struct *lb)
> {
> int retval;
> +
> + if (lb->curr_block > (ext4_lblk_t)-1)
> + return -EOPNOTSUPP;
> +
> /*
> * See if we can add on to the existing range (if it exists)
> */
> --
> 2.51.0
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
© 2016 - 2026 Red Hat, Inc.