[PATCH] hfs: reject volume bitmaps overlapping the MDB
From: Yue Sun
Date: Wed May 27 2026 - 11:34:10 EST
hfs_mdb_commit() writes the volume bitmap while HFS_SB(sb)->mdb_bh is
locked. A crafted image can set drVBMSt so that the bitmap byte range
resolves to the same buffer_head as the MDB. When writeback later calls
lock_buffer() for that bitmap block, the task tries to lock mdb_bh again
and self-deadlocks in __lock_buffer().
Reject the image during mount if the bitmap byte range maps to mdb_bh.
This checks the aliasing condition that can deadlock writeback without
trying to validate unrelated HFS layout fields.
Reported-by: Yue Sun <samsun1006219@xxxxxxxxx>
Closes: https://lore.kernel.org/all/CAEkJfYMB47v1yOWHB8q2dc8kf=uj-rLO=+yMyudwPguJ8Kd3jA@xxxxxxxxxxxxxx/
Signed-off-by: Yue Sun <samsun1006219@xxxxxxxxx>
---
fs/hfs/mdb.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index a97cea35ca2e..abaa636db938 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -99,8 +99,10 @@ int hfs_mdb_get(struct super_block *sb)
char *ptr;
int off2, len, size, sect;
sector_t part_start, part_size;
- loff_t off;
+ sector_t mdb_block, vbm_first, vbm_last;
+ loff_t off, vbm_off;
__be16 attrib;
+ u32 bitmap_size;
/* set the device driver to 512-byte blocks */
size = sb_min_blocksize(sb, HFS_SECTOR_SIZE);
@@ -185,6 +187,25 @@ int hfs_mdb_get(struct super_block *sb)
sb->s_flags |= SB_RDONLY;
}
+ /*
+ * hfs_mdb_commit() writes the bitmap while holding mdb_bh. Reject
+ * images whose bitmap range can resolve to the MDB buffer_head.
+ */
+ bitmap_size = DIV_ROUND_UP(HFS_SB(sb)->fs_ablocks, BITS_PER_BYTE);
+ if (!bitmap_size) {
+ pr_err("bad volume bitmap size\n");
+ return -EIO;
+ }
+ vbm_off = (loff_t)(part_start + be16_to_cpu(mdb->drVBMSt)) <<
+ HFS_SECTOR_SIZE_BITS;
+ vbm_first = vbm_off >> sb->s_blocksize_bits;
+ vbm_last = (vbm_off + bitmap_size - 1) >> sb->s_blocksize_bits;
+ mdb_block = HFS_SB(sb)->mdb_bh->b_blocknr;
+ if (vbm_first <= mdb_block && mdb_block <= vbm_last) {
+ pr_err("volume bitmap overlaps MDB\n");
+ return -EIO;
+ }
+
/* TRY to get the alternate (backup) MDB. */
sect = part_start + part_size - 2;
bh = sb_bread512(sb, sect, mdb2);
--
2.34.1