Skip to content

Commit

Permalink
btrfs: fix inode list leak during backref walking at resolve_indirect…
Browse files Browse the repository at this point in the history
…_refs()

[ Upstream commit 5614dc3 ]

During backref walking, at resolve_indirect_refs(), if we get an error
we jump to the 'out' label and call ulist_free() on the 'parents' ulist,
which frees all the elements in the ulist - however that does not free
any inode lists that may be attached to elements, through the 'aux' field
of a ulist node, so we end up leaking lists if we have any attached to
the unodes.

Fix this by calling free_leaf_list() instead of ulist_free() when we exit
from resolve_indirect_refs(). The static function free_leaf_list() is
moved up for this to be possible and it's slightly simplified by removing
unnecessary code.

Fixes: 3301958 ("Btrfs: add inodes before dropping the extent lock in find_all_leafs")
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
fdmanana authored and gregkh committed Nov 10, 2022
1 parent a80634f commit 6ba3479
Showing 1 changed file with 17 additions and 19 deletions.
36 changes: 17 additions & 19 deletions fs/btrfs/backref.c
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,18 @@ unode_aux_to_inode_list(struct ulist_node *node)
return (struct extent_inode_elem *)(uintptr_t)node->aux;
}

static void free_leaf_list(struct ulist *ulist)
{
struct ulist_node *node;
struct ulist_iterator uiter;

ULIST_ITER_INIT(&uiter);
while ((node = ulist_next(ulist, &uiter)))
free_inode_elem_list(unode_aux_to_inode_list(node));

ulist_free(ulist);
}

/*
* We maintain three separate rbtrees: one for direct refs, one for
* indirect refs which have a key, and one for indirect refs which do not
Expand Down Expand Up @@ -762,7 +774,11 @@ static int resolve_indirect_refs(struct btrfs_fs_info *fs_info,
cond_resched();
}
out:
ulist_free(parents);
/*
* We may have inode lists attached to refs in the parents ulist, so we
* must free them before freeing the ulist and its refs.
*/
free_leaf_list(parents);
return ret;
}

Expand Down Expand Up @@ -1412,24 +1428,6 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
return ret;
}

static void free_leaf_list(struct ulist *blocks)
{
struct ulist_node *node = NULL;
struct extent_inode_elem *eie;
struct ulist_iterator uiter;

ULIST_ITER_INIT(&uiter);
while ((node = ulist_next(blocks, &uiter))) {
if (!node->aux)
continue;
eie = unode_aux_to_inode_list(node);
free_inode_elem_list(eie);
node->aux = 0;
}

ulist_free(blocks);
}

/*
* Finds all leafs with a reference to the specified combination of bytenr and
* offset. key_list_head will point to a list of corresponding keys (caller must
Expand Down

0 comments on commit 6ba3479

Please sign in to comment.