[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20260131140438.2296273-1-shardul.b@mpiricsoftware.com>
Date: Sat, 31 Jan 2026 19:34:38 +0530
From: Shardul Bankar <shardulsb08@...il.com>
To: slava@...eyko.com,
glaubitz@...sik.fu-berlin.de,
frank.li@...o.com,
linux-fsdevel@...r.kernel.org,
linux-kernel@...r.kernel.org
Cc: janak@...ricsoftware.com,
shardulsb08@...il.com,
Shardul Bankar <shardul.b@...ricsoftware.com>,
syzbot+1c8ff72d0cd8a50dfeaa@...kaller.appspotmail.com
Subject: [PATCH v3] hfsplus: validate btree bitmap during mount and handle corruption gracefully
Add bitmap validation during HFS+ btree open to detect corruption where
node 0 (header node) is not marked allocated.
Syzkaller reported issues with corrupted HFS+ images where the btree
allocation bitmap indicates that the header node is free. Node 0 must
always be allocated as it contains the btree header record and the
allocation bitmap itself. Violating this invariant can lead to kernel
panics or undefined behavior when the filesystem attempts to allocate
blocks or manipulate the btree.
The validation checks the node allocation bitmap in the btree header
node (record #2) and verifies that bit 7 (MSB) of the first byte is
set.
Implementation details:
- Perform validation inside hfs_btree_open() to allow identifying the
specific tree (Extents, Catalog, or Attributes) involved.
- Use hfs_bnode_find() and hfs_brec_lenoff() to safely access the
bitmap record using existing infrastructure, ensuring correct handling
of multi-page nodes and endianness.
- If corruption is detected, print a warning identifying the specific
btree and force the filesystem to mount read-only (SB_RDONLY).
This prevents kernel panics from corrupted syzkaller-generated images
while enabling data recovery by allowing the mount to proceed in
read-only mode rather than failing completely.
Reported-by: syzbot+1c8ff72d0cd8a50dfeaa@...kaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=1c8ff72d0cd8a50dfeaa
Link: https://lore.kernel.org/all/b78c1e380a17186b73bc8641b139eca56a8de964.camel@ibm.com/
Signed-off-by: Shardul Bankar <shardul.b@...ricsoftware.com>
---
v3:
- Moved validation logic inline into hfs_btree_open() to allow
reporting the specific corrupted tree ID.
- Replaced custom offset calculations with existing hfs_bnode_find()
and hfs_brec_lenoff() infrastructure to handle node sizes and
page boundaries correctly.
- Removed temporary 'btree_bitmap_corrupted' superblock flag; setup
SB_RDONLY directly upon detection.
- Moved logging to hfs_btree_open() to include the specific tree ID in
the warning message
- Used explicit bitwise check (&) instead of test_bit() to ensure
portability. test_bit() bit-numbering is architecture-dependent
(e.g., bit 0 vs bit 7 can swap meanings on BE vs LE), whereas
masking 0x80 consistently targets the MSB required by the HFS+
on-disk format.
v2:
- Fix compiler warning about comparing u16 bitmap_off with PAGE_SIZE which
can exceed u16 maximum on some architectures
- Cast bitmap_off to unsigned int for the PAGE_SIZE comparison to avoid
tautological constant-out-of-range comparison warning.
- Link: https://lore.kernel.org/oe-kbuild-all/202601251011.kJUhBF3P-lkp@intel.com/
fs/hfsplus/btree.c | 27 +++++++++++++++++++++++++++
include/linux/hfs_common.h | 2 ++
2 files changed, 29 insertions(+)
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 229f25dc7c49..ae81608ba3cf 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -135,9 +135,12 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
struct hfs_btree *tree;
struct hfs_btree_header_rec *head;
struct address_space *mapping;
+ struct hfs_bnode *node;
+ u16 len, bitmap_off;
struct inode *inode;
struct page *page;
unsigned int size;
+ u8 bitmap_byte;
tree = kzalloc(sizeof(*tree), GFP_KERNEL);
if (!tree)
@@ -242,6 +245,30 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
kunmap_local(head);
put_page(page);
+
+ /*
+ * Validate bitmap: node 0 (header node) must be marked allocated.
+ */
+
+ node = hfs_bnode_find(tree, 0);
+ if (IS_ERR(node))
+ goto free_inode;
+
+ len = hfs_brec_lenoff(node,
+ HFSPLUS_BTREE_HDR_MAP_REC, &bitmap_off);
+
+ if (len != 0 && bitmap_off >= sizeof(struct hfs_bnode_desc)) {
+ hfs_bnode_read(node, &bitmap_byte, bitmap_off, 1);
+ if (!(bitmap_byte & HFSPLUS_BTREE_NODE0_BIT)) {
+ pr_warn("(%s): Btree 0x%x bitmap corruption detected, forcing read-only.\n",
+ sb->s_id, id);
+ pr_warn("Run fsck.hfsplus to repair.\n");
+ sb->s_flags |= SB_RDONLY;
+ }
+ }
+
+ hfs_bnode_put(node);
+
return tree;
fail_page:
diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h
index dadb5e0aa8a3..8d21d476cb57 100644
--- a/include/linux/hfs_common.h
+++ b/include/linux/hfs_common.h
@@ -510,7 +510,9 @@ struct hfs_btree_header_rec {
#define HFSPLUS_NODE_MXSZ 32768
#define HFSPLUS_ATTR_TREE_NODE_SIZE 8192
#define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3
+#define HFSPLUS_BTREE_HDR_MAP_REC 2 /* Map (bitmap) record in header node */
#define HFSPLUS_BTREE_HDR_USER_BYTES 128
+#define HFSPLUS_BTREE_NODE0_BIT 0x80
/* btree key type */
#define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */
--
2.34.1
Powered by blists - more mailing lists