btrfs: fix use-after-free on rbtree that tracks inodes for auto defrag
authorFilipe Manana <fdmanana@suse.com>
Sun, 15 Sep 2024 19:52:53 +0000 (20:52 +0100)
committerDavid Sterba <dsterba@suse.com>
Tue, 17 Sep 2024 15:35:53 +0000 (17:35 +0200)
When cleaning up defrag inodes at btrfs_cleanup_defrag_inodes(), called
during remount and unmount, we are freeing every node from the rbtree
that tracks inodes for auto defrag using
rbtree_postorder_for_each_entry_safe(), which doesn't modify the tree
itself. So once we unlock the lock that protects the rbtree, we have a
tree pointing to a root that was freed (and a root pointing to freed
nodes, and their children pointing to other freed nodes, and so on).
This makes further access to the tree result in a use-after-free with
unpredictable results.

Fix this by initializing the rbtree to an empty root after the call to
rbtree_postorder_for_each_entry_safe() and before unlocking.

Fixes: 276940915f23 ("btrfs: clear defragmented inodes using postorder in btrfs_cleanup_defrag_inodes()")
Reported-by: syzbot+ad7966ca1f5dd8b001b3@syzkaller.appspotmail.com
Link: https://lore.kernel.org/linux-btrfs/000000000000f9aad406223eabff@google.com/
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/defrag.c

index acf1f39..b95ef44 100644 (file)
@@ -213,6 +213,8 @@ void btrfs_cleanup_defrag_inodes(struct btrfs_fs_info *fs_info)
                                             &fs_info->defrag_inodes, rb_node)
                kmem_cache_free(btrfs_inode_defrag_cachep, defrag);
 
+       fs_info->defrag_inodes = RB_ROOT;
+
        spin_unlock(&fs_info->defrag_inodes_lock);
 }