From: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com>
To: "glaubitz@physik.fu-berlin.de" <glaubitz@physik.fu-berlin.de>,
"contact@gvernon.com" <contact@gvernon.com>,
"slava@dubeyko.com" <slava@dubeyko.com>,
"frank.li@vivo.com" <frank.li@vivo.com>,
"skhan@linuxfoundation.org" <skhan@linuxfoundation.org>,
"linux-fsdevel@vger.kernel.org" <linux-fsdevel@vger.kernel.org>
Cc: "linux-kernel-mentees@lists.linux.dev"
<linux-kernel-mentees@lists.linux.dev>,
"penguin-kernel@i-love.sakura.ne.jp"
<penguin-kernel@i-love.sakura.ne.jp>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"syzbot+97e301b4b82ae803d21b@syzkaller.appspotmail.com"
<syzbot+97e301b4b82ae803d21b@syzkaller.appspotmail.com>
Subject: Re: [PATCH v2 1/2] hfs: Validate CNIDs in hfs_read_inode
Date: Tue, 4 Nov 2025 22:34:15 +0000 [thread overview]
Message-ID: <e13da4581ff5e8230fa5488f6e5d97695fc349b0.camel@ibm.com> (raw)
In-Reply-To: <20251104014738.131872-3-contact@gvernon.com>
On Tue, 2025-11-04 at 01:47 +0000, George Anthony Vernon wrote:
> hfs_read_inode previously did not validate CNIDs read from disk, thereby
> allowing inodes to be constructed with disallowed CNIDs and placed on
> the dirty list, eventually hitting a bug on writeback.
>
> Validate reserved CNIDs according to Apple technical note TN1150.
The TN1150 technical note describes HFS+ file system and it needs to take into
account the difference between HFS and HFS+. So, it is not completely correct
for the case of HFS to follow to the TN1150 technical note as it is.
>
> This issue was discussed at length on LKML previously, the discussion
> is linked below.
>
> Syzbot tested this patch on mainline and the bug did not replicate.
> This patch was regression tested by issuing various system calls on a
> mounted HFS filesystem and validating that file creation, deletion,
> reads and writes all work.
>
> Link: https://lore.kernel.org/all/427fcb57-8424-4e52-9f21-7041b2c4ae5b@
> I-love.SAKURA.ne.jp/T/
> Reported-by: syzbot+97e301b4b82ae803d21b@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=97e301b4b82ae803d21b
> Cc: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
> Tested-by: syzbot+97e301b4b82ae803d21b@syzkaller.appspotmail.com
> Signed-off-by: George Anthony Vernon <contact@gvernon.com>
> ---
> fs/hfs/inode.c | 67 +++++++++++++++++++++++++++++++++++++++-----------
> 1 file changed, 53 insertions(+), 14 deletions(-)
>
> diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
> index 9cd449913dc8..bc346693941d 100644
> --- a/fs/hfs/inode.c
> +++ b/fs/hfs/inode.c
> @@ -321,6 +321,38 @@ static int hfs_test_inode(struct inode *inode, void *data)
> }
> }
>
> +/*
> + * is_valid_cnid
> + *
> + * Validate the CNID of a catalog record
> + */
> +static inline
> +bool is_valid_cnid(u32 cnid, u8 type)
> +{
> + if (likely(cnid >= HFS_FIRSTUSER_CNID))
> + return true;
> +
> + switch (cnid) {
> + case HFS_ROOT_CNID:
> + return type == HFS_CDR_DIR;
> + case HFS_EXT_CNID:
> + case HFS_CAT_CNID:
> + return type == HFS_CDR_FIL;
> + case HFS_POR_CNID:
> + /* No valid record with this CNID */
> + break;
> + case HFS_BAD_CNID:
HFS is ancient file system that was needed to work with floppy disks. And bad
sectors management was regular task and responsibility of HFS for the case of
floppy disks (HDD was also not very reliable at that times). So, HFS implements
the bad block management. It means that, potentially, Linux kernel could need to
mount a file system volume that created by ancient Mac OS.
I don't think that it's correct management of HFS_BAD_CNID. We must to expect to
have such CNID for the case of HFS.
> + case HFS_EXCH_CNID:
> + /* Not implemented */
> + break;
> + default:
> + /* Invalid reserved CNID */
> + break;
> + }
> +
> + return false;
> +}
> +
> /*
> * hfs_read_inode
> */
> @@ -350,6 +382,8 @@ static int hfs_read_inode(struct inode *inode, void *data)
> rec = idata->rec;
> switch (rec->type) {
> case HFS_CDR_FIL:
> + if (!is_valid_cnid(rec->file.FlNum, HFS_CDR_FIL))
> + goto make_bad_inode;
> if (!HFS_IS_RSRC(inode)) {
> hfs_inode_read_fork(inode, rec->file.ExtRec, rec->file.LgLen,
> rec->file.PyLen, be16_to_cpu(rec->file.ClpSize));
> @@ -371,6 +405,8 @@ static int hfs_read_inode(struct inode *inode, void *data)
> inode->i_mapping->a_ops = &hfs_aops;
> break;
> case HFS_CDR_DIR:
> + if (!is_valid_cnid(rec->dir.DirID, HFS_CDR_DIR))
> + goto make_bad_inode;
> inode->i_ino = be32_to_cpu(rec->dir.DirID);
> inode->i_size = be16_to_cpu(rec->dir.Val) + 2;
> HFS_I(inode)->fs_blocks = 0;
> @@ -380,8 +416,12 @@ static int hfs_read_inode(struct inode *inode, void *data)
> inode->i_op = &hfs_dir_inode_operations;
> inode->i_fop = &hfs_dir_operations;
> break;
> + make_bad_inode:
> + pr_warn("rejected cnid %lu. Volume is probably corrupted, try performing fsck.\n", inode->i_ino);
The "invalid cnid" could sound more relevant than "rejected cnid" for my taste.
The whole message is too long. What's about to have two messages here?
pr_warn("invalid cnid %lu\n", inode->i_ino);
pr_warn("Volume is probably corrupted, try performing fsck.\n");
> + fallthrough;
> default:
> make_bad_inode(inode);
> + break;
> }
> return 0;
> }
> @@ -441,20 +481,19 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
> if (res)
> return res;
>
> - if (inode->i_ino < HFS_FIRSTUSER_CNID) {
> - switch (inode->i_ino) {
> - case HFS_ROOT_CNID:
> - break;
> - case HFS_EXT_CNID:
> - hfs_btree_write(HFS_SB(inode->i_sb)->ext_tree);
> - return 0;
> - case HFS_CAT_CNID:
> - hfs_btree_write(HFS_SB(inode->i_sb)->cat_tree);
> - return 0;
> - default:
> - BUG();
> - return -EIO;
> - }
> + if (!is_valid_cnid(inode->i_ino,
> + S_ISDIR(inode->i_mode) ? HFS_CDR_DIR : HFS_CDR_FIL))
What's about to introduce static inline function or local variable for
S_ISDIR(inode->i_mode) ? HFS_CDR_DIR : HFS_CDR_FIL? I don't like this two line
implementation.
> + BUG();
I am completely against of leaving BUG() here. Several fixes of syzbot issues
were the exchanging BUG() on returning error code. I don't want to investigate
the another syzbot issue that will involve this BUG() here. Let's return error
code here.
Usually, it makes sense to have BUG() for debug mode and to return error code
for the case of release mode. But we don't have the debug mode for HFS code.
Thanks,
Slava.
> +
> + switch (inode->i_ino) {
> + case HFS_EXT_CNID:
> + hfs_btree_write(HFS_SB(inode->i_sb)->ext_tree);
> + return 0;
> + case HFS_CAT_CNID:
> + hfs_btree_write(HFS_SB(inode->i_sb)->cat_tree);
> + return 0;
> + default:
> + break;
> }
>
> if (HFS_IS_RSRC(inode))
next prev parent reply other threads:[~2025-11-04 22:34 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-03 2:45 [PATCH] hfs: Validate CNIDs in hfs_read_inode George Anthony Vernon
2025-10-03 22:40 ` Viacheslav Dubeyko
2025-10-04 1:25 ` George Anthony Vernon
2025-10-07 13:40 ` Viacheslav Dubeyko
2025-10-09 12:57 ` Tetsuo Handa
2025-10-29 3:20 ` George Anthony Vernon
2025-10-29 10:06 ` Tetsuo Handa
2025-11-04 1:47 ` [PATCH v2 0/2] hfs: Validate CNIDs read from filesystem George Anthony Vernon
2025-11-04 1:47 ` [PATCH v2 1/2] hfs: Validate CNIDs in hfs_read_inode George Anthony Vernon
2025-11-04 22:34 ` Viacheslav Dubeyko [this message]
2025-11-11 0:00 ` George Anthony Vernon
2025-11-11 0:48 ` Viacheslav Dubeyko
2025-11-24 22:33 ` George Anthony Vernon
2025-11-25 19:02 ` Viacheslav Dubeyko
2025-11-11 14:39 ` Tetsuo Handa
2025-11-11 22:42 ` Viacheslav Dubeyko
2025-11-24 23:46 ` George Anthony Vernon
2025-11-25 19:15 ` Viacheslav Dubeyko
2025-11-30 10:07 ` Tetsuo Handa
2026-01-06 10:21 ` Tetsuo Handa
2025-11-04 1:47 ` [PATCH v2 2/2] hfs: Update sanity check of the root record George Anthony Vernon
2025-11-04 23:01 ` Viacheslav Dubeyko
2025-11-10 23:03 ` George Anthony Vernon
2025-11-10 23:34 ` Viacheslav Dubeyko
2025-11-11 0:23 ` George Anthony Vernon
2025-11-11 0:34 ` Viacheslav Dubeyko
2025-11-24 22:56 ` George Anthony Vernon
2025-11-11 14:26 ` Tetsuo Handa
2025-11-11 22:56 ` Viacheslav Dubeyko
2025-11-14 14:18 ` Tetsuo Handa
2025-11-14 21:00 ` Viacheslav Dubeyko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e13da4581ff5e8230fa5488f6e5d97695fc349b0.camel@ibm.com \
--to=slava.dubeyko@ibm.com \
--cc=contact@gvernon.com \
--cc=frank.li@vivo.com \
--cc=glaubitz@physik.fu-berlin.de \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel-mentees@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=penguin-kernel@i-love.sakura.ne.jp \
--cc=skhan@linuxfoundation.org \
--cc=slava@dubeyko.com \
--cc=syzbot+97e301b4b82ae803d21b@syzkaller.appspotmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox