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

1sh1ro posted 1 patch 1 week, 1 day ago
Only 0 patches received!
[BUG][PATCH 0/1] hfs: crafted image aliases typeless internal B-tree inode into VFS open path -> kernel BUG in may_open()
Posted by 1sh1ro 1 week, 1 day ago
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