diff options
Diffstat (limited to '0001-btrfs-fix-block-group-leak-when-removing-fails.patch')
-rw-r--r-- | 0001-btrfs-fix-block-group-leak-when-removing-fails.patch | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/0001-btrfs-fix-block-group-leak-when-removing-fails.patch b/0001-btrfs-fix-block-group-leak-when-removing-fails.patch new file mode 100644 index 000000000..71c60cc30 --- /dev/null +++ b/0001-btrfs-fix-block-group-leak-when-removing-fails.patch @@ -0,0 +1,94 @@ +From f6033c5e333238f299c3ae03fac8cc1365b23b77 Mon Sep 17 00:00:00 2001 +From: Xiyu Yang <xiyuyang19@fudan.edu.cn> +Date: Tue, 21 Apr 2020 10:54:11 +0800 +Subject: [PATCH] btrfs: fix block group leak when removing fails + +btrfs_remove_block_group() invokes btrfs_lookup_block_group(), which +returns a local reference of the block group that contains the given +bytenr to "block_group" with increased refcount. + +When btrfs_remove_block_group() returns, "block_group" becomes invalid, +so the refcount should be decreased to keep refcount balanced. + +The reference counting issue happens in several exception handling paths +of btrfs_remove_block_group(). When those error scenarios occur such as +btrfs_alloc_path() returns NULL, the function forgets to decrease its +refcnt increased by btrfs_lookup_block_group() and will cause a refcnt +leak. + +Fix this issue by jumping to "out_put_group" label and calling +btrfs_put_block_group() when those error scenarios occur. + +CC: stable@vger.kernel.org # 4.4+ +Signed-off-by: Xiyu Yang <xiyuyang19@fudan.edu.cn> +Signed-off-by: Xin Tan <tanxin.ctf@gmail.com> +Reviewed-by: David Sterba <dsterba@suse.com> +Signed-off-by: David Sterba <dsterba@suse.com> +--- + fs/btrfs/block-group.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c +index af9e9a008724..696f47103cfc 100644 +--- a/fs/btrfs/block-group.c ++++ b/fs/btrfs/block-group.c +@@ -916,7 +916,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + path = btrfs_alloc_path(); + if (!path) { + ret = -ENOMEM; +- goto out; ++ goto out_put_group; + } + + /* +@@ -954,7 +954,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + ret = btrfs_orphan_add(trans, BTRFS_I(inode)); + if (ret) { + btrfs_add_delayed_iput(inode); +- goto out; ++ goto out_put_group; + } + clear_nlink(inode); + /* One for the block groups ref */ +@@ -977,13 +977,13 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + + ret = btrfs_search_slot(trans, tree_root, &key, path, -1, 1); + if (ret < 0) +- goto out; ++ goto out_put_group; + if (ret > 0) + btrfs_release_path(path); + if (ret == 0) { + ret = btrfs_del_item(trans, tree_root, path); + if (ret) +- goto out; ++ goto out_put_group; + btrfs_release_path(path); + } + +@@ -1102,9 +1102,9 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + + ret = remove_block_group_free_space(trans, block_group); + if (ret) +- goto out; ++ goto out_put_group; + +- btrfs_put_block_group(block_group); ++ /* Once for the block groups rbtree */ + btrfs_put_block_group(block_group); + + ret = btrfs_search_slot(trans, root, &key, path, -1, 1); +@@ -1127,6 +1127,10 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, + /* once for the tree */ + free_extent_map(em); + } ++ ++out_put_group: ++ /* Once for the lookup reference */ ++ btrfs_put_block_group(block_group); + out: + if (remove_rsv) + btrfs_delayed_refs_rsv_release(fs_info, 1); +-- +2.26.2 + |