]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
f2fs: Add sanity checks before unlinking and loading inodes
authorNikola Z. Ivanov <zlatistiv@gmail.com>
Wed, 5 Nov 2025 11:09:43 +0000 (13:09 +0200)
committerJaegeuk Kim <jaegeuk@kernel.org>
Thu, 4 Dec 2025 02:00:03 +0000 (02:00 +0000)
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>
fs/f2fs/inode.c
fs/f2fs/namei.c

index 8c4eafe9ffac045d24060233bffbd02b3efe97f4..e2405b79b3cc2034b0ebd6a3c2b5975d41aa1d80 100644 (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",
index 0245a2800170a10cee8112b68e7b0a0b93f7a2a1..1530314e8b923cacd7a21b4ae0356401fcc1049f 100644 (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;