mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 18:21:24 +02:00
btrfs: free-space-tree: reject mismatched extent and bitmap items
btrfs_load_free_space_tree() reads FREE_SPACE_INFO once and then chooses the bitmap or extent loader for all following free-space records until the next FREE_SPACE_INFO item. Those loaders currently enforce the selected record type only with ASSERT(). On production builds without CONFIG_BTRFS_ASSERT, a malformed free-space tree can therefore be decoded in the wrong mode. An EXTENT item can reach btrfs_free_space_test_bit() as bitmap data, while a BITMAP item can be added as a full free extent. The latter corrupts the in-memory free-space cache and the former can read beyond the item payload. Sanitizer validation reported: general protection fault Call trace: assert_eb_folio_uptodate() (fs/btrfs/extent_io.c:4134) extent_buffer_test_bit() (?:?) btrfs_free_space_test_bit() (fs/btrfs/free-space-tree.c:518) srso_alias_return_thunk() (arch/x86/include/asm/nospec-branch.h:375) __entry_text_end() (?:?) __asan_memcpy() (mm/kasan/shadow.c:103) read_extent_buffer() (?:?) load_free_space_bitmaps() (fs/btrfs/free-space-tree.c:1548) btrfs_get_32() (fs/btrfs/free-space-tree.c:?) btrfs_set_16() (fs/btrfs/free-space-tree.c:?) kmem_cache_alloc_noprof() (?:?) btrfs_load_free_space_tree() (fs/btrfs/free-space-tree.c:1685) load_free_space_tree_for_test() (?:?) rcu_disable_urgency_upon_qs() (kernel/rcu/tree.c:721) vprintk_emit() (?:?) __up_write() (kernel/locking/rwsem.c:1401) clone_commit_root_for_test() (?:?) test_extent_as_bitmap_mode_mismatch() (?:?) kmem_cache_free() (?:?) btrfs_free_path() (fs/btrfs/free-space-tree.c:1449) __add_block_group_free_space() (fs/btrfs/free-space-tree.c:20) run_test() (?:?) do_raw_spin_unlock() (?:?) btrfs_test_free_space_tree() (fs/btrfs/tests/free-space-tree-tests.c:547) btrfs_test_qgroups() (fs/btrfs/tests/qgroup-tests.c:462) btrfs_run_sanity_tests() (fs/btrfs/free-space-tree.c:?) init_btrfs_fs() (fs/btrfs/super.c:2690) do_one_initcall() (init/main.c:1382) __kasan_kmalloc() (?:?) rcu_is_watching() (?:?) do_initcalls() (init/main.c:1457) kernel_init_freeable() (init/main.c:1674) kernel_init() (init/main.c:1584) ret_from_fork() (?:?) __switch_to() (?:?) ret_from_fork_asm() (?:?) Validate every post-info key before decoding it. Reject keys whose type does not match the mode selected by FREE_SPACE_INFO, and reject keys whose range extends past the block group, returning -EUCLEAN instead of feeding the wrong record type to the bitmap or extent decoder. Also reject zero-length FREE_SPACE_EXTENT items in tree-checker, matching the existing FREE_SPACE_BITMAP zero-length check. This keeps the loader range check simple and prevents a zero-length extent item from being a valid on-disk free-space record. Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Zhang Cen <rollkingzzc@gmail.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
f84f833a72
commit
42b00a651a
|
|
@ -1545,6 +1545,29 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int validate_free_space_key(struct btrfs_block_group *block_group,
|
||||
const struct btrfs_key *key, u8 expected_type)
|
||||
{
|
||||
const u64 end = btrfs_block_group_end(block_group);
|
||||
|
||||
if (unlikely(key->type != expected_type)) {
|
||||
btrfs_err(block_group->fs_info,
|
||||
"block group %llu has unexpected free space key type %u, expected %u",
|
||||
block_group->start, key->type, expected_type);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
if (unlikely(key->objectid + key->offset > end)) {
|
||||
btrfs_err(block_group->fs_info,
|
||||
"block group %llu has invalid free space key (%llu %u %llu)",
|
||||
block_group->start, key->objectid, key->type,
|
||||
key->offset);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl,
|
||||
struct btrfs_path *path,
|
||||
u32 expected_extent_count)
|
||||
|
|
@ -1576,8 +1599,9 @@ static int load_free_space_bitmaps(struct btrfs_caching_control *caching_ctl,
|
|||
if (key.type == BTRFS_FREE_SPACE_INFO_KEY)
|
||||
break;
|
||||
|
||||
ASSERT(key.type == BTRFS_FREE_SPACE_BITMAP_KEY);
|
||||
ASSERT(key.objectid < end && key.objectid + key.offset <= end);
|
||||
ret = validate_free_space_key(block_group, &key, BTRFS_FREE_SPACE_BITMAP_KEY);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
offset = key.objectid;
|
||||
while (offset < key.objectid + key.offset) {
|
||||
|
|
@ -1633,7 +1657,6 @@ static int load_free_space_extents(struct btrfs_caching_control *caching_ctl,
|
|||
struct btrfs_fs_info *fs_info = block_group->fs_info;
|
||||
struct btrfs_root *root;
|
||||
struct btrfs_key key;
|
||||
const u64 end = btrfs_block_group_end(block_group);
|
||||
u64 total_found = 0;
|
||||
u32 extent_count = 0;
|
||||
int ret;
|
||||
|
|
@ -1654,8 +1677,9 @@ static int load_free_space_extents(struct btrfs_caching_control *caching_ctl,
|
|||
if (key.type == BTRFS_FREE_SPACE_INFO_KEY)
|
||||
break;
|
||||
|
||||
ASSERT(key.type == BTRFS_FREE_SPACE_EXTENT_KEY);
|
||||
ASSERT(key.objectid < end && key.objectid + key.offset <= end);
|
||||
ret = validate_free_space_key(block_group, &key, BTRFS_FREE_SPACE_EXTENT_KEY);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
ret = btrfs_add_new_free_space(block_group, key.objectid,
|
||||
key.objectid + key.offset,
|
||||
|
|
|
|||
|
|
@ -2129,6 +2129,10 @@ static int check_free_space_extent(struct extent_buffer *leaf, struct btrfs_key
|
|||
blocksize, BTRFS_KEY_FMT_VALUE(key));
|
||||
return -EUCLEAN;
|
||||
}
|
||||
if (unlikely(key->offset == 0)) {
|
||||
generic_err(leaf, slot, "free space extent length is 0");
|
||||
return -EUCLEAN;
|
||||
}
|
||||
if (unlikely(btrfs_item_size(leaf, slot) != 0)) {
|
||||
generic_err(leaf, slot,
|
||||
"invalid item size for free space info, has %u expect 0",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user