Add check for inode->i_nlink == 1 for directories during unlink, as their value is decremented twice, which can trigger a warning in drop_nlink. In such case mark the filesystem as corrupted and return from the function call with the relevant failure return value.
Additionally add the check for i_nlink == 1 in sanity_check_inode in order to detect on-disk corruption early. Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=c07d47c7bc68f47b9083 Tested-by: [email protected] Signed-off-by: Nikola Z. Ivanov <[email protected]> --- fs/f2fs/inode.c | 6 ++++++ fs/f2fs/namei.c | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 8c4eafe9ffac..e2405b79b3cc 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -294,6 +294,12 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio) return false; } + if (S_ISDIR(inode->i_mode) && unlikely(inode->i_nlink == 1)) { + f2fs_warn(sbi, "%s: directory inode (ino=%lx) has a single i_nlink", + __func__, inode->i_ino); + return false; + } + if (f2fs_has_extra_attr(inode)) { if (!f2fs_sb_has_extra_attr(sbi)) { f2fs_warn(sbi, "%s: inode (ino=%lx) is with extra_attr, but extra_attr feature is off", diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 40cf80fd9d9a..849cfdc57b30 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -570,12 +570,13 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) } if (unlikely(inode->i_nlink == 0)) { - f2fs_warn(F2FS_I_SB(inode), "%s: inode (ino=%lx) has zero i_nlink", + f2fs_warn(sbi, "%s: inode (ino=%lx) has zero i_nlink", __func__, inode->i_ino); - err = -EFSCORRUPTED; - set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK); - f2fs_folio_put(folio, false); - goto out; + goto corrupted; + } else if (S_ISDIR(inode->i_mode) && unlikely(inode->i_nlink == 1)) { + f2fs_warn(sbi, "%s: directory inode (ino=%lx) has a single i_nlink", + __func__, inode->i_ino); + goto corrupted; } f2fs_balance_fs(sbi, true); @@ -601,6 +602,12 @@ static int f2fs_unlink(struct inode *dir, struct dentry *dentry) if (IS_DIRSYNC(dir)) f2fs_sync_fs(sbi->sb, 1); + + goto out; +corrupted: + err = -EFSCORRUPTED; + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_folio_put(folio, false); out: trace_f2fs_unlink_exit(inode, err); return err; -- 2.51.0 _______________________________________________ Linux-f2fs-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
