Linux filesystem development
 help / color / mirror / Atom feed
* [PATCH 0/2] hfs/hfsplus: harden btree node 0 bitmap corruption handling
@ 2026-08-14  2:31 Tao Yu
  2026-08-14  2:31 ` [PATCH 1/2] hfs: detect node 0 btree map corruption at mount time Tao Yu
  2026-08-14  2:31 ` [PATCH 2/2] hfs/hfsplus: stop btree allocators from reusing node 0 Tao Yu
  0 siblings, 2 replies; 5+ messages in thread
From: Tao Yu @ 2026-08-14  2:31 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: slava, glaubitz, frank.li, linux-kernel,
	syzbot+2bf21610eea63cb2ce93, Tao Yu

Hi,

This series addresses btree bitmap corruption around node 0 in HFS and
HFS+.

Node 0 is the reserved btree header node. If the on-disk bitmap ever
presents node 0 as free, the filesystem is already corrupted and should
not continue normal allocation from that state.

The series does two things:

  1. Port the existing HFS+ style mount-time node 0 bitmap validation to
     HFS, so HFS detects the corrupted btree map earlier and forces the
     filesystem read-only.

  2. Harden the HFS and HFS+ btree allocators so that attempts to
     allocate node 0 are treated as bitmap corruption. Instead of
     continuing into the reserved header node and later tripping the
     "new node 0 already hashed?" warning, the allocator now emits a
     repair hint, forces the filesystem read-only, and aborts the
     allocation.

This keeps the existing warning as a last-resort invariant check, while
moving the actual corruption handling to earlier and more appropriate
control points.

Patch 1 ports the mount-time validation to HFS.
Patch 2 adds the runtime allocator guard to both HFS and HFS+.

Comments are welcome.


Tao Yu (2):
  hfs: detect node 0 btree map corruption at mount time
  hfs/hfsplus: stop btree allocators from reusing node 0

 fs/hfs/btree.c     | 71 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/hfsplus/btree.c | 11 +++++++
 2 files changed, 82 insertions(+)


Thanks,
Tao
-- 
2.34.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] hfs: detect node 0 btree map corruption at mount time
  2026-08-14  2:31 [PATCH 0/2] hfs/hfsplus: harden btree node 0 bitmap corruption handling Tao Yu
@ 2026-08-14  2:31 ` Tao Yu
  2026-08-14  4:10   ` Viacheslav Dubeyko
  2026-08-14  2:31 ` [PATCH 2/2] hfs/hfsplus: stop btree allocators from reusing node 0 Tao Yu
  1 sibling, 1 reply; 5+ messages in thread
From: Tao Yu @ 2026-08-14  2:31 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: slava, glaubitz, frank.li, linux-kernel,
	syzbot+2bf21610eea63cb2ce93, Tao Yu

HFS+ already validates that the btree map keeps node 0 marked as in-use
when the tree is opened. HFS lacks the same check, so a corrupted volume
can proceed past mount with a broken node bitmap and only fail later in
write paths.

Port the node 0 bitmap validation to HFS. If the header node is not
marked as allocated in the on-disk map, warn that the btree bitmap is
corrupted and force the filesystem read-only so users can repair it with
fsck.hfs.

This keeps the HFS mount-time behavior aligned with HFS+ and catches the
corruption closer to where it becomes observable.

Signed-off-by: Tao Yu <tao1.yu@intel.com>
---
 fs/hfs/btree.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 2eb37a2f64e86..14114318ec724 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -15,12 +15,59 @@
 
 #include "btree.h"
 
+static bool hfs_bmap_test_bit(struct hfs_bnode *node, u32 node_bit_idx)
+{
+	u16 rec_idx, off, len;
+	u32 byte_offset;
+	u8 byte, mask;
+
+	if (node->this == 0) {
+		if (node->type != HFS_NODE_HEADER) {
+			pr_err("hfs: invalid btree header node\n");
+			return false;
+		}
+		rec_idx = 2;
+	} else {
+		if (node->type != HFS_NODE_MAP) {
+			pr_err("hfs: invalid btree map node\n");
+			return false;
+		}
+		rec_idx = 0;
+	}
+
+	len = hfs_brec_lenoff(node, rec_idx, &off);
+	if (!len)
+		return false;
+
+	byte_offset = node_bit_idx / BITS_PER_BYTE;
+	if (byte_offset >= len)
+		return false;
+
+	byte = hfs_bnode_read_u8(node, off + byte_offset);
+	mask = 1 << (7 - (node_bit_idx % BITS_PER_BYTE));
+
+	return byte & mask;
+}
+
+static const char *hfs_btree_name(u32 cnid)
+{
+	switch (cnid) {
+	case HFS_EXT_CNID:
+		return "Extents Overflow File";
+	case HFS_CAT_CNID:
+		return "Catalog File";
+	default:
+		return "Unknown B-tree";
+	}
+}
+
 /* Get a reference to a B*Tree and do some initial checks */
 struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp keycmp)
 {
 	struct hfs_btree *tree;
 	struct hfs_btree_header_rec *head;
 	struct address_space *mapping;
+	struct hfs_bnode *node;
 	struct folio *folio;
 	struct buffer_head *bh;
 	unsigned int size;
@@ -155,6 +202,19 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id, btree_keycmp ke
 	kunmap_local(head);
 	folio_unlock(folio);
 	folio_put(folio);
+
+	node = hfs_bnode_find(tree, 0);
+	if (IS_ERR(node))
+		goto free_inode;
+
+	if (!hfs_bmap_test_bit(node, 0)) {
+		pr_warn("(%s): %s (cnid 0x%x) map record invalid or bitmap corruption detected, forcing read-only.\n",
+			sb->s_id, hfs_btree_name(id), id);
+		pr_warn("Run fsck.hfs to repair.\n");
+		sb->s_flags |= SB_RDONLY;
+	}
+
+	hfs_bnode_put(node);
 	return tree;
 
 fail_folio:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] hfs/hfsplus: stop btree allocators from reusing node 0
  2026-08-14  2:31 [PATCH 0/2] hfs/hfsplus: harden btree node 0 bitmap corruption handling Tao Yu
  2026-08-14  2:31 ` [PATCH 1/2] hfs: detect node 0 btree map corruption at mount time Tao Yu
@ 2026-08-14  2:31 ` Tao Yu
  2026-08-14  4:19   ` Viacheslav Dubeyko
  1 sibling, 1 reply; 5+ messages in thread
From: Tao Yu @ 2026-08-14  2:31 UTC (permalink / raw)
  To: linux-fsdevel
  Cc: slava, glaubitz, frank.li, linux-kernel,
	syzbot+2bf21610eea63cb2ce93, Tao Yu

The btree header node is permanently reserved as node 0. If the on-disk
bitmap ever presents node 0 as free, the filesystem is already
corrupted and the allocator must not try to instantiate it again.

Both HFS and HFS+ currently keep scanning the bitmap, set the bit, and
hand node 0 to hfs_bnode_create()/hfsplus_bnode_create(). HFS+ then
hits the "new node 0 already hashed?" warning reported by syzbot, while
HFS risks continuing after the same corruption pattern.

Teach both allocators to treat attempts to allocate node 0 as btree map
corruption. Force the filesystem read-only, emit the existing repair
hint, and abort the allocation before the code reaches the hashed-node
warning.

Reported-by: syzbot+2bf21610eea63cb2ce93@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=2bf21610eea63cb2ce93
Signed-off-by: Tao Yu <tao1.yu@intel.com>
---
 fs/hfs/btree.c     | 11 +++++++++++
 fs/hfsplus/btree.c | 11 +++++++++++
 2 files changed, 22 insertions(+)

diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
index 14114318ec724..9b0b7418ddbd5 100644
--- a/fs/hfs/btree.c
+++ b/fs/hfs/btree.c
@@ -376,6 +376,17 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
 			if (byte != 0xff) {
 				for (m = 0x80, i = 0; i < 8; m >>= 1, i++) {
 					if (!(byte & m)) {
+						if (unlikely(!(idx + i))) {
+							pr_warn("(%s): %s (cnid 0x%x) map record invalid or bitmap corruption detected, forcing read-only.\n",
+								tree->sb->s_id,
+								hfs_btree_name(tree->cnid),
+								tree->cnid);
+							pr_warn("Run fsck.hfs to repair.\n");
+							tree->sb->s_flags |= SB_RDONLY;
+							kunmap_local(data);
+							hfs_bnode_put(node);
+							return ERR_PTR(-EIO);
+						}
 						idx += i;
 						data[off] |= m;
 						set_page_dirty(*pagep);
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 394542a47e600..3ee92248b2409 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -561,6 +561,17 @@ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
 			if (byte != 0xff) {
 				for (m = 0x80, i = 0; i < 8; m >>= 1, i++) {
 					if (!(byte & m)) {
+						if (unlikely(!(idx + i))) {
+							pr_warn("(%s): %s (cnid 0x%x) map record invalid or bitmap corruption detected, forcing read-only.\n",
+								tree->sb->s_id,
+								hfs_btree_name(tree->cnid),
+								tree->cnid);
+							pr_warn("Run fsck.hfsplus to repair.\n");
+							tree->sb->s_flags |= SB_RDONLY;
+							kunmap_local(data);
+							hfs_bnode_put(node);
+							return ERR_PTR(-EIO);
+						}
 						idx += i;
 						data[ctx.off] |= m;
 						set_page_dirty(page);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] hfs: detect node 0 btree map corruption at mount time
  2026-08-14  2:31 ` [PATCH 1/2] hfs: detect node 0 btree map corruption at mount time Tao Yu
@ 2026-08-14  4:10   ` Viacheslav Dubeyko
  0 siblings, 0 replies; 5+ messages in thread
From: Viacheslav Dubeyko @ 2026-08-14  4:10 UTC (permalink / raw)
  To: Tao Yu, linux-fsdevel
  Cc: glaubitz, frank.li, linux-kernel, syzbot+2bf21610eea63cb2ce93

On Fri, 2026-08-14 at 10:31 +0800, Tao Yu wrote:
> HFS+ already validates that the btree map keeps node 0 marked as in-
> use
> when the tree is opened. HFS lacks the same check, so a corrupted
> volume
> can proceed past mount with a broken node bitmap and only fail later
> in
> write paths.
> 
> Port the node 0 bitmap validation to HFS. If the header node is not
> marked as allocated in the on-disk map, warn that the btree bitmap is
> corrupted and force the filesystem read-only so users can repair it
> with
> fsck.hfs.
> 
> This keeps the HFS mount-time behavior aligned with HFS+ and catches
> the
> corruption closer to where it becomes observable.
> 
> Signed-off-by: Tao Yu <tao1.yu@intel.com>
> ---
>  fs/hfs/btree.c | 60
> ++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 60 insertions(+)
> 
> diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
> index 2eb37a2f64e86..14114318ec724 100644
> --- a/fs/hfs/btree.c
> +++ b/fs/hfs/btree.c
> @@ -15,12 +15,59 @@
>  
>  #include "btree.h"
>  
> +static bool hfs_bmap_test_bit(struct hfs_bnode *node, u32
> node_bit_idx)
> +{
> +	u16 rec_idx, off, len;
> +	u32 byte_offset;
> +	u8 byte, mask;
> +
> +	if (node->this == 0) {
> +		if (node->type != HFS_NODE_HEADER) {
> +			pr_err("hfs: invalid btree header node\n");
> +			return false;
> +		}
> +		rec_idx = 2;
> +	} else {
> +		if (node->type != HFS_NODE_MAP) {
> +			pr_err("hfs: invalid btree map node\n");
> +			return false;
> +		}
> +		rec_idx = 0;
> +	}
> +
> +	len = hfs_brec_lenoff(node, rec_idx, &off);
> +	if (!len)
> +		return false;
> +
> +	byte_offset = node_bit_idx / BITS_PER_BYTE;
> +	if (byte_offset >= len)
> +		return false;
> +
> +	byte = hfs_bnode_read_u8(node, off + byte_offset);
> +	mask = 1 << (7 - (node_bit_idx % BITS_PER_BYTE));
> +
> +	return byte & mask;
> +}
> +
> +static const char *hfs_btree_name(u32 cnid)
> +{
> +	switch (cnid) {
> +	case HFS_EXT_CNID:
> +		return "Extents Overflow File";
> +	case HFS_CAT_CNID:
> +		return "Catalog File";
> +	default:
> +		return "Unknown B-tree";
> +	}
> +}
> +
>  /* Get a reference to a B*Tree and do some initial checks */
>  struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id,
> btree_keycmp keycmp)
>  {
>  	struct hfs_btree *tree;
>  	struct hfs_btree_header_rec *head;
>  	struct address_space *mapping;
> +	struct hfs_bnode *node;
>  	struct folio *folio;
>  	struct buffer_head *bh;
>  	unsigned int size;
> @@ -155,6 +202,19 @@ struct hfs_btree *hfs_btree_open(struct
> super_block *sb, u32 id, btree_keycmp ke
>  	kunmap_local(head);
>  	folio_unlock(folio);
>  	folio_put(folio);
> +
> +	node = hfs_bnode_find(tree, 0);
> +	if (IS_ERR(node))
> +		goto free_inode;
> +
> +	if (!hfs_bmap_test_bit(node, 0)) {
> +		pr_warn("(%s): %s (cnid 0x%x) map record invalid or
> bitmap corruption detected, forcing read-only.\n",
> +			sb->s_id, hfs_btree_name(id), id);
> +		pr_warn("Run fsck.hfs to repair.\n");
> +		sb->s_flags |= SB_RDONLY;
> +	}
> +
> +	hfs_bnode_put(node);
>  	return tree;
>  
>  fail_folio:

Sorry, but this patch [1] already has implemented this functionality.
And this patch is in HFS/HFS+ tree (for-next branch).

Thanks,
Slava.

[1]
https://lore.kernel.org/r/20260716074150.1660-1-aditya.ansh182@gmail.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] hfs/hfsplus: stop btree allocators from reusing node 0
  2026-08-14  2:31 ` [PATCH 2/2] hfs/hfsplus: stop btree allocators from reusing node 0 Tao Yu
@ 2026-08-14  4:19   ` Viacheslav Dubeyko
  0 siblings, 0 replies; 5+ messages in thread
From: Viacheslav Dubeyko @ 2026-08-14  4:19 UTC (permalink / raw)
  To: Tao Yu, linux-fsdevel
  Cc: glaubitz, frank.li, linux-kernel, syzbot+2bf21610eea63cb2ce93

On Fri, 2026-08-14 at 10:31 +0800, Tao Yu wrote:
> The btree header node is permanently reserved as node 0. If the on-
> disk
> bitmap ever presents node 0 as free, the filesystem is already
> corrupted and the allocator must not try to instantiate it again.
> 
> Both HFS and HFS+ currently keep scanning the bitmap, set the bit,
> and
> hand node 0 to hfs_bnode_create()/hfsplus_bnode_create(). HFS+ then
> hits the "new node 0 already hashed?" warning reported by syzbot,
> while
> HFS risks continuing after the same corruption pattern.
> 
> Teach both allocators to treat attempts to allocate node 0 as btree
> map
> corruption. Force the filesystem read-only, emit the existing repair
> hint, and abort the allocation before the code reaches the hashed-
> node
> warning.
> 
> Reported-by: syzbot+2bf21610eea63cb2ce93@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=2bf21610eea63cb2ce93
> Signed-off-by: Tao Yu <tao1.yu@intel.com>
> ---
>  fs/hfs/btree.c     | 11 +++++++++++
>  fs/hfsplus/btree.c | 11 +++++++++++
>  2 files changed, 22 insertions(+)
> 
> diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
> index 14114318ec724..9b0b7418ddbd5 100644
> --- a/fs/hfs/btree.c
> +++ b/fs/hfs/btree.c
> @@ -376,6 +376,17 @@ struct hfs_bnode *hfs_bmap_alloc(struct
> hfs_btree *tree)
>  			if (byte != 0xff) {
>  				for (m = 0x80, i = 0; i < 8; m >>=
> 1, i++) {
>  					if (!(byte & m)) {
> +						if (unlikely(!(idx +
> i))) {
> +							pr_warn("(%s
> ): %s (cnid 0x%x) map record invalid or bitmap corruption detected,
> forcing read-only.\n",
> +								tree
> ->sb->s_id,
> +								hfs_
> btree_name(tree->cnid),
> +								tree
> ->cnid);
> +							pr_warn("Run
> fsck.hfs to repair.\n");
> +							tree->sb-
> >s_flags |= SB_RDONLY;
> +							kunmap_local
> (data);
> +							hfs_bnode_pu
> t(node);
> +							return
> ERR_PTR(-EIO);
> +						}
>  						idx += i;
>  						data[off] |= m;
>  						set_page_dirty(*page
> p);
> diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
> index 394542a47e600..3ee92248b2409 100644
> --- a/fs/hfsplus/btree.c
> +++ b/fs/hfsplus/btree.c
> @@ -561,6 +561,17 @@ struct hfs_bnode *hfs_bmap_alloc(struct
> hfs_btree *tree)
>  			if (byte != 0xff) {
>  				for (m = 0x80, i = 0; i < 8; m >>=
> 1, i++) {
>  					if (!(byte & m)) {
> +						if (unlikely(!(idx +
> i))) {
> +							pr_warn("(%s
> ): %s (cnid 0x%x) map record invalid or bitmap corruption detected,
> forcing read-only.\n",
> +								tree
> ->sb->s_id,
> +								hfs_
> btree_name(tree->cnid),
> +								tree
> ->cnid);
> +							pr_warn("Run
> fsck.hfsplus to repair.\n");
> +							tree->sb-
> >s_flags |= SB_RDONLY;
> +							kunmap_local
> (data);
> +							hfs_bnode_pu
> t(node);
> +							return
> ERR_PTR(-EIO);
> +						}
>  						idx += i;
>  						data[ctx.off] |= m;
>  						set_page_dirty(page)
> ;

We already have check [1] in hfs_btree_open():

	if (!hfs_bmap_test_bit(node, 0)) {
		pr_warn("(%s): %s (cnid 0x%x) map record invalid or
bitmap corruption detected, forcing read-only.\n",
				sb->s_id, hfs_btree_name(id), id);
		pr_warn("Run fsck.hfsplus to repair.\n");
		sb->s_flags |= SB_RDONLY;
	}

The likewise check has been implemented by patch [2] for HFS.

Thanks,
Slava.

[1]
https://elixir.bootlin.com/linux/v7.2-rc6/source/fs/hfsplus/btree.c#L388
[2]
https://lore.kernel.org/r/20260716074150.1660-1-aditya.ansh182@gmail.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-14  4:19 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14  2:31 [PATCH 0/2] hfs/hfsplus: harden btree node 0 bitmap corruption handling Tao Yu
2026-08-14  2:31 ` [PATCH 1/2] hfs: detect node 0 btree map corruption at mount time Tao Yu
2026-08-14  4:10   ` Viacheslav Dubeyko
2026-08-14  2:31 ` [PATCH 2/2] hfs/hfsplus: stop btree allocators from reusing node 0 Tao Yu
2026-08-14  4:19   ` Viacheslav Dubeyko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox