Commit 057495cc authored by Mark Harmstone's avatar Mark Harmstone Committed by David Sterba
Browse files

btrfs: hold block group reference during entire move_existing_remap()



There is a potential use-after-free in move_existing_remap(): we're calling
btrfs_put_block_group() on dest_bg, then passing it to
btrfs_add_block_group_free_space() a few lines later.

Fix this by getting the BG at the start of the function and putting it
near the end. This also means we're not doing a lookup twice for the
same thing.

Reported-by: default avatarChris Mason <clm@fb.com>
Link: https://lore.kernel.org/linux-btrfs/20260125123908.2096548-1-clm@meta.com/


Fixes: bbea42df ("btrfs: move existing remaps before relocating block group")
Reviewed-by: default avatarJohannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: default avatarMark Harmstone <mark@harmstone.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 5118130e
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -4185,6 +4185,8 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
	dest_addr = ins.objectid;
	dest_length = ins.offset;

	dest_bg = btrfs_lookup_block_group(fs_info, dest_addr);

	if (!is_data && !IS_ALIGNED(dest_length, fs_info->nodesize)) {
		u64 new_length = ALIGN_DOWN(dest_length, fs_info->nodesize);

@@ -4295,15 +4297,12 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
	if (unlikely(ret))
		goto end;

	dest_bg = btrfs_lookup_block_group(fs_info, dest_addr);

	adjust_block_group_remap_bytes(trans, dest_bg, dest_length);

	mutex_lock(&dest_bg->free_space_lock);
	bg_needs_free_space = test_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE,
				       &dest_bg->runtime_flags);
	mutex_unlock(&dest_bg->free_space_lock);
	btrfs_put_block_group(dest_bg);

	if (bg_needs_free_space) {
		ret = btrfs_add_block_group_free_space(trans, dest_bg);
@@ -4333,13 +4332,13 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info,
			btrfs_end_transaction(trans);
		}
	} else {
		dest_bg = btrfs_lookup_block_group(fs_info, dest_addr);
		btrfs_free_reserved_bytes(dest_bg, dest_length, 0);
		btrfs_put_block_group(dest_bg);

		ret = btrfs_commit_transaction(trans);
	}

	btrfs_put_block_group(dest_bg);

	return ret;
}