* [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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.