From: Josef Bacik <jo...@toxicpanda.com>

[ Upstream commit 2abc726ab4b83db774e315c660ab8da21477092f ]

We previously were checking if the root had a dead root before accessing
root->reloc_root in order to avoid a use-after-free type bug.  However
this scenario happens after we've unset the reloc control, so we would
have been saved if we'd simply checked for fs_info->reloc_control.  At
this point during relocation we no longer need to be creating new reloc
roots, so simply move this check above the reloc_root checks to avoid
any future races and confusion.

Reviewed-by: Qu Wenruo <w...@suse.com>
Signed-off-by: Josef Bacik <jo...@toxicpanda.com>
Reviewed-by: David Sterba <dste...@suse.com>
Signed-off-by: David Sterba <dste...@suse.com>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 fs/btrfs/relocation.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index af3605a0bf2e0..1313506a7ecb5 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -1468,6 +1468,10 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle 
*trans,
        int clear_rsv = 0;
        int ret;
 
+       if (!rc || !rc->create_reloc_tree ||
+           root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
+               return 0;
+
        /*
         * The subvolume has reloc tree but the swap is finished, no need to
         * create/update the dead reloc tree
@@ -1481,10 +1485,6 @@ int btrfs_init_reloc_root(struct btrfs_trans_handle 
*trans,
                return 0;
        }
 
-       if (!rc || !rc->create_reloc_tree ||
-           root->root_key.objectid == BTRFS_TREE_RELOC_OBJECTID)
-               return 0;
-
        if (!trans->reloc_reserved) {
                rsv = trans->block_rsv;
                trans->block_rsv = rc->block_rsv;
@@ -2336,6 +2336,18 @@ static noinline_for_stack int merge_reloc_root(struct 
reloc_control *rc,
                        trans = NULL;
                        goto out;
                }
+
+               /*
+                * At this point we no longer have a reloc_control, so we can't
+                * depend on btrfs_init_reloc_root to update our last_trans.
+                *
+                * But that's ok, we started the trans handle on our
+                * corresponding fs_root, which means it's been added to the
+                * dirty list.  At commit time we'll still call
+                * btrfs_update_reloc_root() and update our root item
+                * appropriately.
+                */
+               reloc_root->last_trans = trans->transid;
                trans->block_rsv = rc->block_rsv;
 
                replaced = 0;
-- 
2.25.1

Reply via email to