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);