]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fs/ntfs3: cancle set bad inode after removing name fails
authorEdward Adam Davis <eadavis@qq.com>
Wed, 18 Jun 2025 07:31:57 +0000 (15:31 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Aug 2025 10:08:40 +0000 (12:08 +0200)
[ Upstream commit d99208b91933fd2a58ed9ed321af07dacd06ddc3 ]

The reproducer uses a file0 on a ntfs3 file system with a corrupted i_link.
When renaming, the file0's inode is marked as a bad inode because the file
name cannot be deleted.

The underlying bug is that make_bad_inode() is called on a live inode.
In some cases it's "icache lookup finds a normal inode, d_splice_alias()
is called to attach it to dentry, while another thread decides to call
make_bad_inode() on it - that would evict it from icache, but we'd already
found it there earlier".
In some it's outright "we have an inode attached to dentry - that's how we
got it in the first place; let's call make_bad_inode() on it just for shits
and giggles".

Fixes: 78ab59fee07f ("fs/ntfs3: Rework file operations")
Reported-by: syzbot+1aa90f0eb1fc3e77d969@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1aa90f0eb1fc3e77d969
Signed-off-by: Edward Adam Davis <eadavis@qq.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/ntfs3/frecord.c
fs/ntfs3/namei.c
fs/ntfs3/ntfs_fs.h

index 3c876c468c2c47c8ebe9e4b0acfe13e1e435563e..5c45fec832b00bbe900c3620e9905a18b4afeac5 100644 (file)
@@ -3058,8 +3058,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
  * ni_rename - Remove one name and insert new name.
  */
 int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
-             struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
-             bool *is_bad)
+             struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de)
 {
        int err;
        struct NTFS_DE *de2 = NULL;
@@ -3082,8 +3081,8 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
        err = ni_add_name(new_dir_ni, ni, new_de);
        if (!err) {
                err = ni_remove_name(dir_ni, ni, de, &de2, &undo);
-               if (err && ni_remove_name(new_dir_ni, ni, new_de, &de2, &undo))
-                       *is_bad = true;
+               WARN_ON(err && ni_remove_name(new_dir_ni, ni, new_de, &de2,
+                       &undo));
        }
 
        /*
index 61c4da8e6c3de9d463d6cd113448844a73d0e3c1..f5901c23ab93791d364ccba6fe118ede20b25bec 100644 (file)
@@ -261,7 +261,7 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir,
        struct ntfs_inode *ni = ntfs_i(inode);
        struct inode *new_inode = d_inode(new_dentry);
        struct NTFS_DE *de, *new_de;
-       bool is_same, is_bad;
+       bool is_same;
        /*
         * de           - memory of PATH_MAX bytes:
         * [0-1024)     - original name (dentry->d_name)
@@ -330,12 +330,8 @@ static int ntfs_rename(struct mnt_idmap *idmap, struct inode *dir,
        if (dir_ni != new_dir_ni)
                ni_lock_dir2(new_dir_ni);
 
-       is_bad = false;
-       err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de, &is_bad);
-       if (is_bad) {
-               /* Restore after failed rename failed too. */
-               _ntfs_bad_inode(inode);
-       } else if (!err) {
+       err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de);
+       if (!err) {
                simple_rename_timestamp(dir, dentry, new_dir, new_dentry);
                mark_inode_dirty(inode);
                mark_inode_dirty(dir);
index c98e6868bfbadbf8328fa51b3f2da5492ba302d5..72810d8f62ee9dca205946541fdd696c1ca9ac0c 100644 (file)
@@ -577,8 +577,7 @@ int ni_add_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
                struct NTFS_DE *de);
 
 int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
-             struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de,
-             bool *is_bad);
+             struct ntfs_inode *ni, struct NTFS_DE *de, struct NTFS_DE *new_de);
 
 bool ni_is_dirty(struct inode *inode);