Linux EXT4 FS development
 help / color / mirror / Atom feed
From: "Theodore Ts'o" <tytso@mit.edu>
To: Ext4 Developers List <linux-ext4@vger.kernel.org>
Cc: "Theodore Ts'o" <tytso@mit.edu>
Subject: [PATCH 15/27] ext4: check multi-block overlap on left traversal in add_system_zone()
Date: Wed, 11 Nov 2026 23:06:26 -0500	[thread overview]
Message-ID: <20261112040638.1576836-16-tytso@mit.edu> (raw)
In-Reply-To: <20261112040638.1576836-1-tytso@mit.edu>

When walking system_blks->root to insert [start_blk, start_blk + count),
add_system_zone() takes the left branch whenever start_blk <
entry->start_blk instead of checking that the entire range ends at or
before entry->start_blk (unlike ext4_sb_block_valid(), which checks
start_blk + count - 1 < entry->start_blk).

As a result, a corrupt multi-block metadata range (such as a group's
inode table or a reserved journal inode extent mapped in
ext4_protect_reserved_inode() before sbi->s_system_blks is published)
that starts strictly before an existing system zone and extends into or
across it traverses rb_left instead of returning -EFSCORRUPTED. This
inserts overlapping intervals into system_blks->root and breaks the
disjoint-interval invariant relied upon by ext4_sb_block_valid() (for
example, a lookup on blocks past entry->start_blk + entry->count
branches right and misses the overlapping zone stored in rb_left). A
corrupt 64-bit start_blk whose start_blk + count wraps around u64 can
similarly corrupt the rbtree ordering.

Reject zero-length or u64-wrapping ranges up front and require
start_blk + count <= entry->start_blk before taking rb_left in
add_system_zone(). Add KUnit regression tests in
fs/ext4/block_validity-test.c covering group metadata and journal
extents that partially overlap from the left, completely enclose an
existing system zone, or wrap 64-bit block numbers.

Fixes: bf9a379d0980 ("ext4: don't allow overlapping system zones")
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/block_validity-test.c | 68 +++++++++++++++++++++++++++++++++++
 fs/ext4/block_validity.c      |  5 ++-
 2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/block_validity-test.c b/fs/ext4/block_validity-test.c
index 5d3dadba1328..5b50f56b3453 100644
--- a/fs/ext4/block_validity-test.c
+++ b/fs/ext4/block_validity-test.c
@@ -1111,6 +1111,27 @@ static const struct bv_corrupt_meta_case bv_corrupt_meta_cases[] = {
 		.ibitmap = 2050,
 		.itable = 2051,
 	},
+	{
+		.desc = "itable_starts_before_and_overlaps_bbitmap",
+		.grp = 0,
+		.bbitmap = 20,
+		.ibitmap = 40,
+		.itable = 15,
+	},
+	{
+		.desc = "itable_starts_before_and_encloses_ibitmap",
+		.grp = 2,
+		.bbitmap = 50,
+		.ibitmap = 55,
+		.itable = 52,
+	},
+	{
+		.desc = "itable_u64_wrap_around_overflow",
+		.grp = 2,
+		.bbitmap = 2049,
+		.ibitmap = 2050,
+		.itable = U64_MAX - 2ULL,
+	},
 };
 
 KUNIT_ARRAY_PARAM_DESC(bv_corrupt_meta, bv_corrupt_meta_cases, desc);
@@ -1314,6 +1335,52 @@ static void test_journal_inode_overlap_sets_fs_error(struct kunit *test)
 	KUNIT_EXPECT_NULL(test, rcu_access_pointer(ctx->sbi.s_system_blks));
 }
 
+static void test_journal_extent_left_overlap_rejected(struct kunit *test)
+{
+	struct bv_test_ctx *ctx = test->priv;
+	struct inode *j_inode;
+
+	/*
+	 * Place group 2's block bitmap at block 20 (ino = 0) and map a
+	 * multi-block journal extent at [18..21] that starts strictly before
+	 * block 20 and extends across it. During ext4_setup_system_zone(),
+	 * s_system_blks is still NULL when ext4_map_blocks() runs, so
+	 * add_system_zone() must catch this left-traversal overlap.
+	 */
+	bv_set_group_meta(ctx->sb, &ctx->gdps[2], 20ULL, 2050ULL, 2051ULL);
+	ext4_set_feature_journal(ctx->sb);
+	ctx->es.s_journal_inum = cpu_to_le32(EXT4_JOURNAL_INO);
+
+	j_inode = bv_create_inode(test, ctx, EXT4_JOURNAL_INO,
+				  S_IFREG | 0600, true);
+	i_size_write(j_inode, 4 * BV_TEST_BLK_SIZE);
+	bv_append_extent(test, j_inode, 0, 18ULL, 4);
+
+	KUNIT_EXPECT_EQ(test, ext4_setup_system_zone(ctx->sb), -EFSCORRUPTED);
+	KUNIT_EXPECT_TRUE(test,
+			  (bool)(ctx->sbi.s_mount_state & EXT4_ERROR_FS));
+	KUNIT_EXPECT_EQ(test, le32_to_cpu(ctx->es.s_last_error_ino),
+			(u32)EXT4_JOURNAL_INO);
+	KUNIT_EXPECT_EQ(test, ctx->es.s_last_error_errcode,
+			EXT4_ERR_EFSCORRUPTED);
+	KUNIT_EXPECT_NULL(test, rcu_access_pointer(ctx->sbi.s_system_blks));
+
+	/*
+	 * Also verify a journal extent [1020..1040] that completely encloses
+	 * group 1's entire metadata zone [1025..1038].
+	 */
+	bv_clear_fs_error(ctx);
+	ext4_es_remove_extent(j_inode, 0, EXT_MAX_BLOCKS);
+	ext_inode_hdr(j_inode)->eh_entries = 0;
+	i_size_write(j_inode, 21 * BV_TEST_BLK_SIZE);
+	bv_append_extent(test, j_inode, 0, 1020ULL, 21);
+
+	KUNIT_EXPECT_EQ(test, ext4_setup_system_zone(ctx->sb), -EFSCORRUPTED);
+	KUNIT_EXPECT_TRUE(test,
+			  (bool)(ctx->sbi.s_mount_state & EXT4_ERROR_FS));
+	KUNIT_EXPECT_NULL(test, rcu_access_pointer(ctx->sbi.s_system_blks));
+}
+
 /* --- Test 10: ext4_check_blockref and Indirect Macros --- */
 
 static void test_check_blockref_valid_and_sparse(struct kunit *test)
@@ -1498,6 +1565,7 @@ static struct kunit_case ext4_block_validity_test_cases[] = {
 	KUNIT_CASE(test_journal_inode_iget_error),
 	KUNIT_CASE(test_journal_inode_map_blocks_error),
 	KUNIT_CASE(test_journal_inode_overlap_sets_fs_error),
+	KUNIT_CASE(test_journal_extent_left_overlap_rejected),
 	KUNIT_CASE(test_check_blockref_valid_and_sparse),
 	KUNIT_CASE(test_check_blockref_corrupt_entries_and_journal_bypass),
 	KUNIT_CASE(test_ind_check_inode_and_indirect_blockref),
diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c
index 53b042f36a69..34468932e461 100644
--- a/fs/ext4/block_validity.c
+++ b/fs/ext4/block_validity.c
@@ -75,10 +75,13 @@ static int add_system_zone(struct ext4_system_blocks *system_blks,
 	struct rb_node **n = &system_blks->root.rb_node, *node;
 	struct rb_node *parent = NULL, *new_node;
 
+	if (!count || (start_blk + count < start_blk))
+		return -EFSCORRUPTED;
+
 	while (*n) {
 		parent = *n;
 		entry = rb_entry(parent, struct ext4_system_zone, node);
-		if (start_blk < entry->start_blk)
+		if (start_blk + count <= entry->start_blk)
 			n = &(*n)->rb_left;
 		else if (start_blk >= (entry->start_blk + entry->count))
 			n = &(*n)->rb_right;
-- 
2.56.0.542.g3ef89b7020-goog


  parent reply	other threads:[~2026-11-12  4:07 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12  4:06 [PATCH 00/27] ext4: add lots of KUnit tests! Theodore Ts'o
2026-11-12  4:06 ` [PATCH 01/27] ext4: add KUinit tests for fs/ext4/extents_status.c Theodore Ts'o
2026-11-12  4:18   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 02/27] ext4: add KUnit tests for fs/ext4/extents.c Theodore Ts'o
2026-11-12  4:28   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 03/27] ext4: add KUnit tests for ext4_ext_split() leaf and index splitting Theodore Ts'o
2026-11-12  4:15   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 04/27] ext4: add KUnit tests for fs/ext4/xattr.c Theodore Ts'o
2026-11-12  4:26   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 05/27] ext4: add KUnit tests for fs/ext4/namei.c Theodore Ts'o
2026-11-12  4:21   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 06/27] ext4: expand KUnit tests for fs/ext4/mballoc.c Theodore Ts'o
2026-11-12  4:26   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 07/27] ext4: add KUnit tests for fs/ext4/inline.c Theodore Ts'o
2026-11-12  4:29   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 08/27] ext4: zero s_fc_replay_state in ext4_fc_replay_cleanup() Theodore Ts'o
2026-11-12  4:16   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 09/27] ext4: add KUnit tests for fs/ext4/fast_commit.c Theodore Ts'o
2026-11-12  4:28   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 10/27] ext4: expand KUnit tests for fs/ext4/inode.c Theodore Ts'o
2026-11-12  4:36   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 11/27] ext4: add KUnit tests for fs/ext4/ialloc.c Theodore Ts'o
2026-11-12  4:25   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 12/27] ext4: add KUnit tests for fs/ext4/balloc.c Theodore Ts'o
2026-11-12  4:16   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 13/27] ext4: add KUnit tests for fs/ext4/orphan.c Theodore Ts'o
2026-11-12  4:18   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 14/27] ext4: add KUnit tests for fs/ext4/block_validity.c Theodore Ts'o
2026-11-12  4:21   ` sashiko-bot
2026-11-12  4:06 ` Theodore Ts'o [this message]
2026-11-12  4:10   ` [PATCH 15/27] ext4: check multi-block overlap on left traversal in add_system_zone() sashiko-bot
2026-11-12  4:06 ` [PATCH 16/27] ext4: enable full feature coverage in fs/ext4/.kunitconfig Theodore Ts'o
2026-11-12  4:24   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 17/27] ext4: add KUnit tests for fs/ext4/super.c Theodore Ts'o
2026-11-12  4:27   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 18/27] ext4: add KUnit tests for fs/ext4/sysfs.c Theodore Ts'o
2026-11-12  4:32   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 19/27] ext4: add KUnit tests for fs/ext4/ioctl.c Theodore Ts'o
2026-11-12  4:35   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 20/27] ext4: add KUnit tests for fs/ext4/resize.c Theodore Ts'o
2026-11-12  4:34   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 21/27] ext4: add KUnit tests for fs/ext4/migrate.c Theodore Ts'o
2026-11-12  4:37   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 22/27] ext4: add KUnit tests for fs/ext4/indirect.c Theodore Ts'o
2026-11-12  4:46   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 23/27] ext4: expand KUnit tests in fs/ext4/extents-test.c for file range operations Theodore Ts'o
2026-11-12  4:34   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 24/27] ext4: add KUnit tests for fs/ext4/fsmap.c Theodore Ts'o
2026-11-12  4:30   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 25/27] ext4: add KUnit tests for fs/ext4/move_extent.c Theodore Ts'o
2026-11-12  4:36   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 26/27] ext4: add KUnit tests for fs/ext4/mmp.c Theodore Ts'o
2026-11-12  4:29   ` sashiko-bot
2026-11-12  4:06 ` [PATCH 27/27] ext4: add KUnit tests for fs/ext4/dir.c Theodore Ts'o
2026-11-12  4:41   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261112040638.1576836-16-tytso@mit.edu \
    --to=tytso@mit.edu \
    --cc=linux-ext4@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox