[BUG][PATCH 0/1] hfs: crafted image aliases typeless internal B-tree inode into VFS open path -> kernel BUG in may_open()

From: 1sh1ro

Date: Wed Sep 16 2026 - 05:11:47 EST


Hi,

While fuzzing mainline v7.2-rc7 (KASAN + lockdep + CONFIG_DEBUG_VFS=y)
we hit a deterministic panic-class kernel BUG in the generic VFS open
path, reachable through the HFS filesystem with a single crafted image:

VFS_BUG_ON_INODE(!IS_ANON_FILE(inode)): inode:ffff8880206c5740
fs:hfs mode:0 opflags:0xc flags:0x0 state:0x0 count:2
------------[ cut here ]------------
kernel BUG at fs/namei.c:4271!
RIP: 0010:may_open+0x2ba/0x480 fs/namei.c:4271
Call Trace:
do_open fs/namei.c:4698 [inline]
path_openat+0x1600/0x3de0 fs/namei.c:4863
do_file_open+0x216/0x470 fs/namei.c:4892
do_sys_openat2+0xe8/0x250 fs/open.c:1368
__x64_sys_openat+0x13f/0x1f0 fs/open.c:1385

Root cause (verified against source):

1. hfs_btree_open() (fs/hfs/btree.c) creates the extents/catalog
B-tree inodes with iget_locked(sb, HFS_EXT_CNID / HFS_CAT_CNID)
and never assigns a file type; hfs_inode_read_fork() does not
touch i_mode either. Both internal inodes therefore stay cached
with i_mode == 0 after every mount.

2. A crafted catalog record whose DirID/FlNum equals 3 or 4 makes
hfs_lookup() -> hfs_iget() -> iget5_locked() return that cached
inode (hfs_test_inode() only compares i_ino), bypassing
hfs_read_inode() entirely.

3. openat() on the resulting dentry reaches may_open() with no
S_IFMT bits set, hits the 'default:' case added by the
CONFIG_DEBUG_VFS series (1e7ab6f67824) and panics.

Note on visibility: with !CONFIG_DEBUG_VFS the assertion compiles away,
so the same image merely yields an openable typeless inode - which is
presumably why this has never shown up on syzbot. Current mainline
(7.3-rc) still carries the unfixed hfs code; the issue is not
regressed, it is simply old.

Patch [1/1] fixes it in two steps:

- hfs_btree_open(): give the internal B-tree inodes an S_IFREG type,
restoring the VFS "valid inode type" invariant for every cached
inode no matter what the image contains;

- hfs_iget(): refuse catalog records addressing the reserved B-tree
CNIDs (NULL per the existing convention: mount failure / -EACCES).

A self-contained syzkaller reproducer (embeds the crafted image,
triggers in under a second) and the full C repro are available on
request.

Environment:

Kernel: 7.2.0-rc7-00232-gdcb68831eac7 (v7.2-rc7 based)
Config: KASAN (inline), KCOV, lockdep, CONFIG_DEBUG_VFS=y, SMP PREEMPT
QEMU (KVM), x86_64

Best regards,
1sh1ro