btrfs: Check each block group has corresponding chunk at mount time
PATCH v2 1/2 btrfs: Check each block group has corresponding chunk at mount time
A crafted btrfs with incorrect chunk<->block group mapping, it could leads to a lot of unexpected behavior.
Although the crafted image can be catched by block group item checker added in "[PATCH] btrfs: tree-checker: Verify block_group_item", if one crafted a valid enough block group item which can pass above check but still mismatch with existing chunk, it could cause a lot of undefined behavior.
This patch will add extra block group -> chunk mapping check, to ensure we have a completely matching (start, len, flags) chunk for each block group at mount time.
Here we reuse the original find_first_block_group(), which is already doing basic bg -> chunk check, adding more check on start/len and type flags.
---
changelog:
v2:
Reuse existing find_first_block_group() to do the verification, pointed out by Gu.
--- fs/btrfs/extent-tree.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)
fs/btrfs/extent-tree.c +28 -1
@@ -9717,6 +9717,8 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info,int ret =0 ;struct btrfs_key found_key;struct extent_buffer *leaf; +struct btrfs_block_group_item bg; +u64 flags;int slot; ret =btrfs_search_slot (NULL , root, key, path,0 ,0 );
@@ -9751,8 +9753,33 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info,"logical %llu len %llu found bg but no related chunk" , found_key.objectid, found_key.offset); ret = -ENOENT ; + }else if (em->start != found_key.objectid || + em->len != found_key.offset) { +btrfs_err (fs_info, +"block group %llu len %llu mismatch with chunk %llu len %llu" , + found_key.objectid, found_key.offset, + em->start, em->len); + ret = -EUCLEAN ; }else { - ret =0 ; +read_extent_buffer (leaf, &bg, +btrfs_item_ptr_offset (leaf, slot), +sizeof (bg)); + flags =btrfs_block_group_flags (&bg) & +BTRFS_BLOCK_GROUP_TYPE_MASK ; + +if (flags != (em->map_lookup->type & +BTRFS_BLOCK_GROUP_TYPE_MASK )) { +btrfs_err (fs_info, +"block group %llu len %llu type flags 0x%llx mismatch with chunk type flags 0x%llx" , + found_key.objectid, + found_key.offset, + flags, + (BTRFS_BLOCK_GROUP_TYPE_MASK & + em->map_lookup->type)); + ret = -EUCLEAN ; + }else { + ret =0 ; + } }free_extent_map (em); goto out;
Signature · 1 line
2.18.0
PATCH v2 2/2 btrfs: Verify every chunk has corresponding block group at mount time
If a crafted btrfs has missing block group items, it could cause unexpected behavior and breaks our expectation on 1:1 chunk<->block group mapping.
Although we added block group -> chunk mapping check, we still need chunk -> block group mapping check.
This patch will do extra check to ensure each chunk has its corresponding block group.
---
changelog:
v2:
Update return value, explain the @len parameter for lookup_extent_mapping(), and remove the ratelimit, all suggested by David.
--- fs/btrfs/extent-tree.c | 57 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-)
fs/btrfs/extent-tree.c +56 -1
@@ -10030,6 +10030,61 @@ btrfs_create_block_group_cache(struct btrfs_fs_info *fs_info,return cache; } + +/* +* Iterate all chunks and verify each of them has corresponding block group +*/ +static int check_chunk_block_group_mappings (struct btrfs_fs_info *fs_info) +{ +struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree; +struct extent_map *em; +struct btrfs_block_group_cache *bg; +u64 start =0 ; +int ret =0 ; + +while (1 ) { +read_lock (&map_tree->map_tree.lock); +/* +* lookup_extent_mapping will return the first extent map +* intersects the range, so set @len to 1 is enough to get +* the first chunk. +*/ + em =lookup_extent_mapping (&map_tree->map_tree, start,1 ); +read_unlock (&map_tree->map_tree.lock); +if (!em) +break ; + + bg =btrfs_lookup_block_group (fs_info, em->start); +if (!bg) { +btrfs_err (fs_info, +"chunk start=%llu len=%llu doesn't have corresponding block group" , + em->start, em->len); + ret = -EUCLEAN ; +free_extent_map (em); +break ; + } +if (bg->key.objectid != em->start || + bg->key.offset != em->len || + (bg->flags &BTRFS_BLOCK_GROUP_TYPE_MASK ) != + (em->map_lookup->type &BTRFS_BLOCK_GROUP_TYPE_MASK )) { +btrfs_err (fs_info, +"chunk start=%llu len=%llu flags=0x%llx doesn't match with block group start=%llu len=%llu flags=0x%llx" , + em->start, em->len, + em->map_lookup->type &BTRFS_BLOCK_GROUP_TYPE_MASK , + bg->key.objectid, bg->key.offset, + bg->flags &BTRFS_BLOCK_GROUP_TYPE_MASK ); + ret = -EUCLEAN ; +free_extent_map (em); +btrfs_put_block_group (bg); +break ; + } + start = em->start + em->len; +free_extent_map (em); +btrfs_put_block_group (bg); + } +return ret; +} +int btrfs_read_block_groups (struct btrfs_fs_info *info) {struct btrfs_path *path;
@@ -10203,7 +10258,7 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info)btrfs_add_raid_kobjects (info);init_global_block_rsv (info); - ret =0 ; + ret =check_chunk_block_group_mappings (info); error:btrfs_free_path (path);return ret;
Signature · 1 line
2.18.0
Quoted 76 lines
-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Qu Wenruo
Sent: Friday, July 06, 2018 1:36 PM
To: [email protected]
Subject: [PATCH v2 1/2] btrfs: Check each block group has corresponding chunk at mount timeA crafted btrfs with incorrect chunk<->block group mapping, it could leads to a lot of unexpected behavior.
Although the crafted image can be catched by block group item checker added in "[PATCH] btrfs: tree-checker: Verify
block_group_item", if one crafted a valid enough block group item which can pass above check but still mismatch with existing chunk, it could cause a lot of undefined behavior.This patch will add extra block group -> chunk mapping check, to ensure we have a completely matching (start, len, flags) chunk for each block group at mount time.
Here we reuse the original
find_first_block_group(), which is already doing basic bg -> chunk check, adding more check on start/len and type flags.Reported-by Xu Wen <[email protected]>Signed-off-by Qu Wenruo <[email protected]>---
changelog:
v2:
Reuse existingfind_first_block_group()to do the verification, pointed out by Gu.--- fs/btrfs/extent-tree.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)fs/btrfs/extent-tree.c +22
-1@@ -9717,6 +9717,8 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info,int ret =0 ;struct btrfs_key found_key;struct extent_buffer *leaf; +struct btrfs_block_group_item bg; +u64 flags;int slot; ret =btrfs_search_slot (NULL , root, key, path,0 ,0 );@@ -9751,8 +9753,33 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info,"logical %llu len %llu found bg but no related chunk" , found_key.objectid, found_key.offset); ret = -ENOENT ; + }else if (em->start != found_key.objectid || + em->len != found_key.offset) { +btrfs_err (fs_info, +"block group %llu len %llu mismatch with chunk %llu len %llu" , + found_key.objectid, found_key.offset, + em->start, em->len); + ret = -EUCLEAN ; }else { - ret =0 ; +read_extent_buffer (leaf, &bg, +btrfs_item_ptr_offset (leaf, slot), +sizeof (bg)); + flags =btrfs_block_group_flags (&bg) & +BTRFS_BLOCK_GROUP_TYPE_MASK ; + +if (flags != (em->map_lookup->type & +BTRFS_BLOCK_GROUP_TYPE_MASK )) { + btrfs_err(fs_info, +"block group %llu len %llu type flags 0x%llx mismatch with chunk type flags 0x%llx", + found_key.objectid, + found_key.offset, + flags,
fs/btrfs/extent-tree.c static int find_first_block_group(struct btrfs_fs_info *fs_info,
Quoted 10 lines
fs/btrfs/extent-tree.c +6
-1+ (BTRFS_BLOCK_GROUP_TYPE_MASK & + em->map_lookup->type)); + ret = -EUCLEAN ; + }else { + ret =0 ; + } }free_extent_map (em); goto out; --
Quoted 7 lines
2.18.0
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
�翳簕.n�+壏煯壄�+%娝�遍荻w�簕.n�+壏{饼籯~镶炟^n噐■�侂h櫒璀�&{鄗夸z罐楘+€�+zf"穐殘啳嗃i��飦�戧鐉_璁鎗:+v墾�)撸鴐