btrfs: relocation: Only remove reloc rb_trees if reloc control has been initialized
authorQu Wenruo <wqu@suse.com>
Tue, 3 Jul 2018 09:10:07 +0000 (17:10 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 15 Sep 2018 07:45:32 +0000 (09:45 +0200)
[ Upstream commit 389305b2aa68723c754f88d9dbd268a400e10664 ]

Invalid reloc tree can cause kernel NULL pointer dereference when btrfs
does some cleanup of the reloc roots.

It turns out that fs_info::reloc_ctl can be NULL in
btrfs_recover_relocation() as we allocate relocation control after all
reloc roots have been verified.
So when we hit: note, we haven't called set_reloc_control() thus
fs_info::reloc_ctl is still NULL.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=199833
Reported-by: Xu Wen <wen.xu@gatech.edu>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Tested-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/btrfs/relocation.c

index 9841faef08ea72d0b50aa3ad3c881bd27bd7fc76..b80b03e0c5d3f3f2b70b4cb7bef64450c889a38a 100644 (file)
@@ -1334,18 +1334,19 @@ static void __del_reloc_root(struct btrfs_root *root)
        struct mapping_node *node = NULL;
        struct reloc_control *rc = fs_info->reloc_ctl;
 
-       spin_lock(&rc->reloc_root_tree.lock);
-       rb_node = tree_search(&rc->reloc_root_tree.rb_root,
-                             root->node->start);
-       if (rb_node) {
-               node = rb_entry(rb_node, struct mapping_node, rb_node);
-               rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
+       if (rc) {
+               spin_lock(&rc->reloc_root_tree.lock);
+               rb_node = tree_search(&rc->reloc_root_tree.rb_root,
+                                     root->node->start);
+               if (rb_node) {
+                       node = rb_entry(rb_node, struct mapping_node, rb_node);
+                       rb_erase(&node->rb_node, &rc->reloc_root_tree.rb_root);
+               }
+               spin_unlock(&rc->reloc_root_tree.lock);
+               if (!node)
+                       return;
+               BUG_ON((struct btrfs_root *)node->data != root);
        }
-       spin_unlock(&rc->reloc_root_tree.lock);
-
-       if (!node)
-               return;
-       BUG_ON((struct btrfs_root *)node->data != root);
 
        spin_lock(&fs_info->trans_lock);
        list_del_init(&root->root_list);