>From 40db09869bfabf51593f9a638aff09c72d9c8f1e Mon Sep 17 00:00:00 2001 From: George Anthony Vernon Date: Fri, 3 Oct 2025 00:32:06 +0100 Subject: [PATCH] hfs: Validate CNIDs in hfs_read_inode hfs_read_inode previously did not validate CNIDs read from disk, thereby allowing bad inodes to be placed on the dirty list and written back. Validate reserved CNIDs according to Apple technical note TN1150. Reported-by: syzbot+97e301b4b82ae803d21b@syzkaller.appspotmail.com Signed-off-by: George Anthony Vernon --- fs/hfs/inode.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index a81ce7a740b9..ab71493cf501 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -310,6 +310,34 @@ static int hfs_test_inode(struct inode *inode, void *data) } } +/* + * is_valid_cnid + * + * Validate the catalog number of an inode read from disk + */ +static bool is_valid_cnid(unsigned long cnid, s8 type) +{ + if (likely(cnid >= HFS_FIRSTUSER_CNID)) + return true; + + switch (cnid) { + case HFS_POR_CNID: + return type == HFS_CDR_DIR; + case HFS_ROOT_CNID: + return type == HFS_CDR_DIR; + case HFS_EXT_CNID: + return type == HFS_CDR_FIL; + case HFS_CAT_CNID: + return type == HFS_CDR_FIL; + case HFS_BAD_CNID: + return type == HFS_CDR_FIL; + case HFS_EXCH_CNID: + return type == HFS_CDR_FIL; + default: + return false; + } +} + /* * hfs_read_inode */ @@ -348,6 +376,11 @@ static int hfs_read_inode(struct inode *inode, void *data) } inode->i_ino = be32_to_cpu(rec->file.FlNum); + if (!is_valid_cnid(inode->i_ino, HFS_CDR_FIL)) { + printk(KERN_WARNING "hfs: rejected cnid %lu\n", inode->i_ino); + make_bad_inode(inode); + break; + } inode->i_mode = S_IRUGO | S_IXUGO; if (!(rec->file.Flags & HFS_FIL_LOCK)) inode->i_mode |= S_IWUGO; @@ -361,6 +394,11 @@ static int hfs_read_inode(struct inode *inode, void *data) break; case HFS_CDR_DIR: inode->i_ino = be32_to_cpu(rec->dir.DirID); + if (!is_valid_cnid(inode->i_ino, HFS_CDR_DIR)) { + printk(KERN_WARNING "hfs: rejected cnid %lu\n", inode->i_ino); + make_bad_inode(inode); + break; + } inode->i_size = be16_to_cpu(rec->dir.Val) + 2; HFS_I(inode)->fs_blocks = 0; inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask); -- 2.50.1