btrfs: check block group before marking it unused in balance_remap_chunks()

Fix a potential segfault in balance_remap_chunks(): if we quit early
because btrfs_inc_block_group_ro() fails, all the remaining items in the
chunks list will still have their bg value set to NULL. It's thus not
safe to dereference this pointer without checking first.

Reported-by: Chris Mason <clm@fb.com>
Link: https://lore.kernel.org/linux-btrfs/20260125120717.1578828-1-clm@meta.com/
Fixes: 81e5a4551c ("btrfs: allow balancing remap tree")
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Mark Harmstone <mark@harmstone.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Mark Harmstone 2026-02-19 19:19:00 +00:00 committed by David Sterba
parent 057495ccc0
commit adbb0ebacc

View File

@ -4277,20 +4277,29 @@ static int balance_remap_chunks(struct btrfs_fs_info *fs_info, struct btrfs_path
end:
while (!list_empty(chunks)) {
bool is_unused;
struct btrfs_block_group *bg;
rci = list_first_entry(chunks, struct remap_chunk_info, list);
spin_lock(&rci->bg->lock);
is_unused = !btrfs_is_block_group_used(rci->bg);
spin_unlock(&rci->bg->lock);
bg = rci->bg;
if (bg) {
/*
* This is a bit racy and the 'used' status can change
* but this is not a problem as later functions will
* verify it again.
*/
spin_lock(&bg->lock);
is_unused = !btrfs_is_block_group_used(bg);
spin_unlock(&bg->lock);
if (is_unused)
btrfs_mark_bg_unused(rci->bg);
if (is_unused)
btrfs_mark_bg_unused(bg);
if (rci->made_ro)
btrfs_dec_block_group_ro(rci->bg);
if (rci->made_ro)
btrfs_dec_block_group_ro(bg);
btrfs_put_block_group(rci->bg);
btrfs_put_block_group(bg);
}
list_del(&rci->list);
kfree(rci);