Commit a8df6fe6 authored by Filipe Manana's avatar Filipe Manana Committed by Chris Mason

Btrfs: fix setup_leaf_for_split() to avoid leaf corruption

We were incorrectly detecting when the target key didn't exist anymore
after releasing the path and re-searching the tree. This could make
us split or duplicate (btrfs_split_item() and btrfs_duplicate_item()
are its only callers at the moment) an item when we should not.

For the case of duplicating an item, we currently only duplicate
checksum items (csum tree) and file extent items (fs/subvol trees).
For the checksum items we end up overriding the item completely,
but for file extent items we update only some of their fields in
the copy (done in __btrfs_drop_extents), which means we can end up
having a logical corruption for some values.

Also for the case where we duplicate a file extent item it will make
us produce a leaf with a wrong key order, as btrfs_duplicate_item()
advances us to the next slot and then its caller sets a smaller key
on the new item at that slot (like in __btrfs_drop_extents() e.g.).
Alternatively if the tree search in setup_leaf_for_split() leaves
with path->slots[0] == btrfs_header_nritems(path->nodes[0]), we end
up accessing beyond the leaf's end (when we check if the item's size
has changed) and make our caller insert an item at the invalid slot
btrfs_header_nritems(path->nodes[0]) + 1, causing an invalid memory
access if the leaf is full or nearly full.

This issue has been present since the introduction of this function
in 2009:

    Btrfs: Add btrfs_duplicate_item
    commit ad48fd75Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 57bbddd7
...@@ -4353,13 +4353,15 @@ static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans, ...@@ -4353,13 +4353,15 @@ static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans,
path->search_for_split = 1; path->search_for_split = 1;
ret = btrfs_search_slot(trans, root, &key, path, 0, 1); ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
path->search_for_split = 0; path->search_for_split = 0;
if (ret > 0)
ret = -EAGAIN;
if (ret < 0) if (ret < 0)
goto err; goto err;
ret = -EAGAIN; ret = -EAGAIN;
leaf = path->nodes[0]; leaf = path->nodes[0];
/* if our item isn't there or got smaller, return now */ /* if our item isn't there, return now */
if (ret > 0 || item_size != btrfs_item_size_nr(leaf, path->slots[0])) if (item_size != btrfs_item_size_nr(leaf, path->slots[0]))
goto err; goto err;
/* the leaf has changed, it now has room. return now */ /* the leaf has changed, it now has room. return now */
......
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