]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
btrfs: fix delayed_node ref_tracker use after free
authorLeo Martins <loemra.dev@gmail.com>
Mon, 20 Oct 2025 23:16:15 +0000 (16:16 -0700)
committerDavid Sterba <dsterba@suse.com>
Wed, 22 Oct 2025 07:40:04 +0000 (09:40 +0200)
Move the print before releasing the delayed node.

In my initial testing there was a bug that was causing delayed_nodes
to not get freed which is why I put the print after the release. This
obviously neglects the case where the delayed node is properly freed.

Add condition to make sure we only print if we have more than one
reference to the delayed_node to prevent printing when we only have
the reference taken in btrfs_kill_all_delayed_nodes().

Fixes: b767a28d6154 ("btrfs: print leaked references in kill_all_delayed_nodes()")
Tested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Leo Martins <loemra.dev@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/delayed-inode.c
fs/btrfs/delayed-inode.h

index 41e37f7f67cc01c5d10382f1cc26ff4b9967f628..3df7b9d7fbe8d3854087c569b4d1e9309a517dd3 100644 (file)
@@ -2110,9 +2110,9 @@ void btrfs_kill_all_delayed_nodes(struct btrfs_root *root)
 
                for (int i = 0; i < count; i++) {
                        __btrfs_kill_delayed_node(delayed_nodes[i]);
+                       btrfs_delayed_node_ref_tracker_dir_print(delayed_nodes[i]);
                        btrfs_release_delayed_node(delayed_nodes[i],
                                                   &delayed_node_trackers[i]);
-                       btrfs_delayed_node_ref_tracker_dir_print(delayed_nodes[i]);
                }
        }
 }
index 0d949edc0caf16eb48e31f1ff3984074dbdf05d5..b09d4ec8c77dde24cb7a29afd5ac4ee073c05d0c 100644 (file)
@@ -219,6 +219,13 @@ static inline void btrfs_delayed_node_ref_tracker_dir_print(struct btrfs_delayed
        if (!btrfs_test_opt(node->root->fs_info, REF_TRACKER))
                return;
 
+       /*
+        * Only print if there are leaked references. The caller is
+        * holding one reference, so if refs == 1 there is no leak.
+        */
+       if (refcount_read(&node->refs) == 1)
+               return;
+
        ref_tracker_dir_print(&node->ref_dir.dir,
                              BTRFS_DELAYED_NODE_REF_TRACKER_DISPLAY_LIMIT);
 }