[lvc-project] [PATCH] ext2: fix use-after-free in read_block_bitmap()

Denis Zubov d.zubov at tssltd.ru
Sun Apr 26 12:17:42 MSK 2026


When bh_read() returns 0 (buffer already uptodate), read_block_bitmap()
skips ext2_valid_block_bitmap() and returns the buffer_head without
validation. On a corrupt filesystem with an invalid block bitmap this
allows a corrupt bh to propagate to ext2_try_to_allocate(), where
ext2_set_bit_atomic() writes to bitmap_bh->b_data of an already freed
page, causing a use-after-free write.

The comment 'continue with corrupt bitmap' suggests this was intentional,
however continuing with an invalid bitmap leads to memory corruption.
The correct behavior on a corrupt filesystem is to return NULL so all
callers handle the error safely via the existing io_error path.

Fix by always running ext2_valid_block_bitmap() before returning bh,
regardless of whether the buffer was cached or freshly read, and
returning NULL with brelse() on validation failure.

Found by Linux Verification Center (linuxtesting.org) with Syzkaller.

Signed-off-by: Denis Zubov <d.zubov at tssltd.ru>
---
 fs/ext2/balloc.c | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index b8cfab8f98b9..3345a25b4bde 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -146,23 +146,19 @@ read_block_bitmap(struct super_block *sb, unsigned int block_group)
 		return NULL;
 	}
 	ret = bh_read(bh, 0);
-	if (ret > 0)
-		return bh;
-	if (ret < 0) {
-		brelse(bh);
-		ext2_error(sb, __func__,
-			    "Cannot read block bitmap - "
-			    "block_group = %d, block_bitmap = %u",
-			    block_group, le32_to_cpu(desc->bg_block_bitmap));
-		return NULL;
-	}
-
-	ext2_valid_block_bitmap(sb, desc, block_group, bh);
-	/*
-	 * file system mounted not to panic on error, continue with corrupt
-	 * bitmap
-	 */
-	return bh;
+        if (ret < 0) {
+                brelse(bh);
+                ext2_error(sb, __func__,
+                            "Cannot read block bitmap - "
+                            "block_group = %d, block_bitmap = %u",
+                            block_group, le32_to_cpu(desc->bg_block_bitmap));
+                return NULL;
+        }
+        if (!ext2_valid_block_bitmap(sb, desc, block_group, bh)) {
+                brelse(bh);
+                return NULL;
+        }
+        return bh;
 }
 
 static void group_adjust_blocks(struct super_block *sb, int group_no,
-- 
2.53.0




More information about the lvc-project mailing list