| From foo@baz Tue Sep 11 12:05:55 CEST 2018 |
| From: Qu Wenruo <wqu@suse.com> |
| Date: Tue, 3 Jul 2018 17:10:07 +0800 |
| Subject: btrfs: relocation: Only remove reloc rb_trees if reloc control has been initialized |
| |
| From: Qu Wenruo <wqu@suse.com> |
| |
| [ 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 | 23 ++++++++++++----------- |
| 1 file changed, 12 insertions(+), 11 deletions(-) |
| |
| --- a/fs/btrfs/relocation.c |
| +++ b/fs/btrfs/relocation.c |
| @@ -1321,18 +1321,19 @@ static void __del_reloc_root(struct btrf |
| 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); |