Commit bf53d468 authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba

btrfs: only search for left_info if there is no right_info in try_merge_free_space

In try_to_merge_free_space we attempt to find entries to the left and
right of the entry we are adding to see if they can be merged.  We
search for an entry past our current info (saved into right_info), and
then if right_info exists and it has a rb_prev() we save the rb_prev()
into left_info.

However there's a slight problem in the case that we have a right_info,
but no entry previous to that entry.  At that point we will search for
an entry just before the info we're attempting to insert.  This will
simply find right_info again, and assign it to left_info, making them
both the same pointer.

Now if right_info _can_ be merged with the range we're inserting, we'll
add it to the info and free right_info.  However further down we'll
access left_info, which was right_info, and thus get a use-after-free.

Fix this by only searching for the left entry if we don't find a right
entry at all.

The CVE referenced had a specially crafted file system that could
trigger this use-after-free. However with the tree checker improvements
we no longer trigger the conditions for the UAF.  But the original
conditions still apply, hence this fix.

Reference: CVE-2019-19448
Fixes: 96303081 ("Btrfs: use hybrid extents+bitmap rb tree for free space")
CC: stable@vger.kernel.org # 4.4+
Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 1e6e238c
...@@ -2282,7 +2282,7 @@ static int insert_into_bitmap(struct btrfs_free_space_ctl *ctl, ...@@ -2282,7 +2282,7 @@ static int insert_into_bitmap(struct btrfs_free_space_ctl *ctl,
static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl, static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl,
struct btrfs_free_space *info, bool update_stat) struct btrfs_free_space *info, bool update_stat)
{ {
struct btrfs_free_space *left_info; struct btrfs_free_space *left_info = NULL;
struct btrfs_free_space *right_info; struct btrfs_free_space *right_info;
bool merged = false; bool merged = false;
u64 offset = info->offset; u64 offset = info->offset;
...@@ -2298,7 +2298,7 @@ static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl, ...@@ -2298,7 +2298,7 @@ static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl,
if (right_info && rb_prev(&right_info->offset_index)) if (right_info && rb_prev(&right_info->offset_index))
left_info = rb_entry(rb_prev(&right_info->offset_index), left_info = rb_entry(rb_prev(&right_info->offset_index),
struct btrfs_free_space, offset_index); struct btrfs_free_space, offset_index);
else else if (!right_info)
left_info = tree_search_offset(ctl, offset - 1, 0, 0); left_info = tree_search_offset(ctl, offset - 1, 0, 0);
/* See try_merge_free_space() comment. */ /* See try_merge_free_space() comment. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment