btrfs: clear DEAD_RELOC_TREE before dropping the reloc root
authorJosef Bacik <josef@toxicpanda.com>
Fri, 13 Mar 2020 15:44:46 +0000 (11:44 -0400)
committerDavid Sterba <dsterba@suse.com>
Mon, 23 Mar 2020 16:01:58 +0000 (17:01 +0100)
The DEAD_RELOC_TREE flag is in place in order to avoid a use after free
in init_reloc_root, tracking the presence of reloc_root.  However adding
the explicit tree references in previous patches makes the use after
free impossible because at this point we no longer have a reloc_control
set on the fs_info and thus cannot enter the function.

So move this to be coupled with clearing the root->reloc_root so we're
consistent with all other operations of the reloc root.

Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
[ update changelog ]
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/relocation.c

index a0c8f7949a199e263b7ac53ab972d0ffcaf732d4..54724ae4538095013e3f0da5ed53cba0f0d294df 100644 (file)
@@ -2275,18 +2275,18 @@ static int clean_dirty_subvols(struct reloc_control *rc)
 
                        list_del_init(&root->reloc_dirty_list);
                        root->reloc_root = NULL;
-                       if (reloc_root) {
-
-                               ret2 = btrfs_drop_snapshot(reloc_root, 0, 1);
-                               if (ret2 < 0 && !ret)
-                                       ret = ret2;
-                       }
                        /*
                         * Need barrier to ensure clear_bit() only happens after
                         * root->reloc_root = NULL. Pairs with have_reloc_root.
                         */
                        smp_wmb();
                        clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
+
+                       if (reloc_root) {
+                               ret2 = btrfs_drop_snapshot(reloc_root, 0, 1);
+                               if (ret2 < 0 && !ret)
+                                       ret = ret2;
+                       }
                        btrfs_put_root(root);
                } else {
                        /* Orphan reloc tree, just clean it up */