]> www.infradead.org Git - users/hch/dma-mapping.git/commitdiff
nilfs2: fix missing cleanup on rollforward recovery error
authorRyusuke Konishi <konishi.ryusuke@gmail.com>
Sat, 10 Aug 2024 06:52:42 +0000 (15:52 +0900)
committerAndrew Morton <akpm@linux-foundation.org>
Mon, 2 Sep 2024 00:59:00 +0000 (17:59 -0700)
In an error injection test of a routine for mount-time recovery, KASAN
found a use-after-free bug.

It turned out that if data recovery was performed using partial logs
created by dsync writes, but an error occurred before starting the log
writer to create a recovered checkpoint, the inodes whose data had been
recovered were left in the ns_dirty_files list of the nilfs object and
were not freed.

Fix this issue by cleaning up inodes that have read the recovery data if
the recovery routine fails midway before the log writer starts.

Link: https://lkml.kernel.org/r/20240810065242.3701-1-konishi.ryusuke@gmail.com
Fixes: 0f3e1c7f23f8 ("nilfs2: recovery functions")
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Tested-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/nilfs2/recovery.c

index b638dc06df2f72fa0c1c5416ad964a0cdc9eac3a..61e25a980f73ef33c1b52cb5706b586973c4061f 100644 (file)
@@ -715,6 +715,33 @@ static void nilfs_finish_roll_forward(struct the_nilfs *nilfs,
        brelse(bh);
 }
 
+/**
+ * nilfs_abort_roll_forward - cleaning up after a failed rollforward recovery
+ * @nilfs: nilfs object
+ */
+static void nilfs_abort_roll_forward(struct the_nilfs *nilfs)
+{
+       struct nilfs_inode_info *ii, *n;
+       LIST_HEAD(head);
+
+       /* Abandon inodes that have read recovery data */
+       spin_lock(&nilfs->ns_inode_lock);
+       list_splice_init(&nilfs->ns_dirty_files, &head);
+       spin_unlock(&nilfs->ns_inode_lock);
+       if (list_empty(&head))
+               return;
+
+       set_nilfs_purging(nilfs);
+       list_for_each_entry_safe(ii, n, &head, i_dirty) {
+               spin_lock(&nilfs->ns_inode_lock);
+               list_del_init(&ii->i_dirty);
+               spin_unlock(&nilfs->ns_inode_lock);
+
+               iput(&ii->vfs_inode);
+       }
+       clear_nilfs_purging(nilfs);
+}
+
 /**
  * nilfs_salvage_orphan_logs - salvage logs written after the latest checkpoint
  * @nilfs: nilfs object
@@ -773,15 +800,19 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs,
                if (unlikely(err)) {
                        nilfs_err(sb, "error %d writing segment for recovery",
                                  err);
-                       goto failed;
+                       goto put_root;
                }
 
                nilfs_finish_roll_forward(nilfs, ri);
        }
 
- failed:
+put_root:
        nilfs_put_root(root);
        return err;
+
+failed:
+       nilfs_abort_roll_forward(nilfs);
+       goto put_root;
 }
 
 /**