Blame 0001-btrfs-progs-convert-prevent-32bit-overflow-for-cctx-.patch

e4a3d3
From fe8715e1337a1bad5c49e165ab77c033c334efbc Mon Sep 17 00:00:00 2001
e4a3d3
From: Qu Wenruo <wqu@suse.com>
e4a3d3
Date: Mon, 20 Jul 2020 20:51:08 +0800
e4a3d3
Subject: [PATCH 1/2] btrfs-progs: convert: prevent 32bit overflow for
e4a3d3
 cctx->total_bytes
e4a3d3
e4a3d3
[BUG]
e4a3d3
When convert is called on a 64GiB ext4 fs, it fails like this:
e4a3d3
e4a3d3
  $ btrfs-convert  /dev/loop0p1
e4a3d3
  create btrfs filesystem:
e4a3d3
          blocksize: 4096
e4a3d3
          nodesize:  16384
e4a3d3
          features:  extref, skinny-metadata (default)
e4a3d3
          checksum:  crc32c
e4a3d3
  creating ext2 image file
e4a3d3
  ERROR: missing data block for bytenr 1048576
e4a3d3
  ERROR: failed to create ext2_saved/image: -2
e4a3d3
  WARNING: an error occurred during conversion, filesystem is partially created but not finalized and not mountable
e4a3d3
e4a3d3
Btrfs-convert also corrupts the source fs:
e4a3d3
e4a3d3
  $ LANG=C e2fsck /dev/loop0p1 -f
e4a3d3
  e2fsck 1.45.6 (20-Mar-2020)
e4a3d3
  Resize inode not valid.  Recreate<y>? yes
e4a3d3
  Pass 1: Checking inodes, blocks, and sizes
e4a3d3
  Deleted inode 3681 has zero dtime.  Fix<y>? yes
e4a3d3
  Inodes that were part of a corrupted orphan linked list found.  Fix<y>? yes
e4a3d3
  Inode 3744 was part of the orphaned inode list.  FIXED.
e4a3d3
  Deleted inode 3745 has zero dtime.  Fix<y>? yes
e4a3d3
  Inode 3747 has INLINE_DATA_FL flag on filesystem without inline data support.
e4a3d3
  Clear<y>? yes
e4a3d3
  ...
e4a3d3
e4a3d3
[CAUSE]
e4a3d3
After some debugging, the first strange behavior is, the value of
e4a3d3
cctx->total_bytes is 0 in ext2_open_fs().
e4a3d3
e4a3d3
It turns out that, the value assign for cctx->total_bytes could lead to
e4a3d3
bit overflow for the unsigned int value.
e4a3d3
e4a3d3
And that 0 cctx->total_bytes leads to various problems for later free
e4a3d3
space calculation.
e4a3d3
For example, in calculate_available_space(), we use cctx->total_bytes to
e4a3d3
ensure we won't create a data chunk beyond device end:
e4a3d3
e4a3d3
		cue_len = min(cctx->total_bytes - cur_off, cur_len);
e4a3d3
e4a3d3
If that cur_offset is also 0, we will create a cache_extent with 0 size,
e4a3d3
which could cause a lot of problems for cache tree search.
e4a3d3
e4a3d3
[FIX]
e4a3d3
Do manual casting for the multiply operation, so we could got a real u64
e4a3d3
result.  The fix will be applied to all supported fses (ext* and
e4a3d3
reiserfs).
e4a3d3
e4a3d3
Reported-by: Christian Zangl <coralllama@gmail.com>
e4a3d3
Reviewed-by: Nikolay Borisov <nborisov@suse.com>
e4a3d3
Signed-off-by: Qu Wenruo <wqu@suse.com>
e4a3d3
Signed-off-by: David Sterba <dsterba@suse.com>
e4a3d3
(cherry picked from commit 670a19828ad40004d05ad70cdd45d58008d3fb51)
e4a3d3
---
e4a3d3
 convert/main.c            | 1 +
e4a3d3
 convert/source-ext2.c     | 2 +-
e4a3d3
 convert/source-reiserfs.c | 2 +-
e4a3d3
 3 files changed, 3 insertions(+), 2 deletions(-)
e4a3d3
e4a3d3
diff --git a/convert/main.c b/convert/main.c
e4a3d3
index 7709e9a6..df6a2ae3 100644
e4a3d3
--- a/convert/main.c
e4a3d3
+++ b/convert/main.c
e4a3d3
@@ -1136,6 +1136,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
e4a3d3
 	if (ret)
e4a3d3
 		goto fail;
e4a3d3
 
e4a3d3
+	ASSERT(cctx.total_bytes);
e4a3d3
 	blocksize = cctx.blocksize;
e4a3d3
 	total_bytes = (u64)blocksize * (u64)cctx.block_count;
e4a3d3
 	if (blocksize < 4096) {
e4a3d3
diff --git a/convert/source-ext2.c b/convert/source-ext2.c
e4a3d3
index f11ef651..d73684ef 100644
e4a3d3
--- a/convert/source-ext2.c
e4a3d3
+++ b/convert/source-ext2.c
e4a3d3
@@ -87,7 +87,7 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
e4a3d3
 	cctx->fs_data = ext2_fs;
e4a3d3
 	cctx->blocksize = ext2_fs->blocksize;
e4a3d3
 	cctx->block_count = ext2_fs->super->s_blocks_count;
e4a3d3
-	cctx->total_bytes = ext2_fs->blocksize * ext2_fs->super->s_blocks_count;
e4a3d3
+	cctx->total_bytes = (u64)ext2_fs->super->s_blocks_count * ext2_fs->blocksize;
e4a3d3
 	cctx->volume_name = strndup((char *)ext2_fs->super->s_volume_name, 16);
e4a3d3
 	cctx->first_data_block = ext2_fs->super->s_first_data_block;
e4a3d3
 	cctx->inodes_count = ext2_fs->super->s_inodes_count;
e4a3d3
diff --git a/convert/source-reiserfs.c b/convert/source-reiserfs.c
e4a3d3
index 9fd6b9ab..3b4cb5ad 100644
e4a3d3
--- a/convert/source-reiserfs.c
e4a3d3
+++ b/convert/source-reiserfs.c
e4a3d3
@@ -82,7 +82,7 @@ static int reiserfs_open_fs(struct btrfs_convert_context *cxt, const char *name)
e4a3d3
 	cxt->fs_data = fs;
e4a3d3
 	cxt->blocksize = fs->fs_blocksize;
e4a3d3
 	cxt->block_count = get_sb_block_count(fs->fs_ondisk_sb);
e4a3d3
-	cxt->total_bytes = cxt->blocksize * cxt->block_count;
e4a3d3
+	cxt->total_bytes = (u64)cxt->block_count * cxt->blocksize;
e4a3d3
 	cxt->volume_name = strndup(fs->fs_ondisk_sb->s_label, 16);
e4a3d3
 	cxt->first_data_block = 0;
e4a3d3
 	cxt->inodes_count = reiserfs_count_objectids(fs);
e4a3d3
-- 
e4a3d3
2.26.2
e4a3d3