f2fs: Add sanity checks before unlinking and loading inodes

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: syzbot+c07d47c7bc68f47b9083@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c07d47c7bc68f47b9083
Tested-by: syzbot+c07d47c7bc68f47b9083@syzkaller.appspotmail.com
Signed-off-by: Nikola Z. Ivanov <zlatistiv@gmail.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Nikola Z. Ivanov 2025-11-05 13:09:43 +02:00 committed by Jaegeuk Kim
parent 9b3c8336c6
commit f37981edcd
2 changed files with 18 additions and 5 deletions

View File

@ -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",

View File

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