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

Reply via email to