Commit e06cd3dd authored by Liu Bo's avatar Liu Bo Committed by David Sterba

Btrfs: add validadtion checks for chunk loading

To prevent fuzzed filesystem images from panic the whole system,
we need various validation checks to refuse to mount such an image
if btrfs finds any invalid value during loading chunks, including
both sys_array and regular chunks.

Note that these checks may not be sufficient to cover all corner cases,
feel free to add more checks.
Reported-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Reported-by: default avatarQuentin Casasnovas <quentin.casasnovas@oracle.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 99e3ecfc
...@@ -6251,27 +6251,23 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, ...@@ -6251,27 +6251,23 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
return dev; return dev;
} }
static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, /* Return -EIO if any error, otherwise return 0. */
struct extent_buffer *leaf, static int btrfs_check_chunk_valid(struct btrfs_root *root,
struct btrfs_chunk *chunk) struct extent_buffer *leaf,
struct btrfs_chunk *chunk, u64 logical)
{ {
struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
struct map_lookup *map;
struct extent_map *em;
u64 logical;
u64 length; u64 length;
u64 stripe_len; u64 stripe_len;
u64 devid; u16 num_stripes;
u8 uuid[BTRFS_UUID_SIZE]; u16 sub_stripes;
int num_stripes; u64 type;
int ret;
int i;
logical = key->offset;
length = btrfs_chunk_length(leaf, chunk); length = btrfs_chunk_length(leaf, chunk);
stripe_len = btrfs_chunk_stripe_len(leaf, chunk); stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
num_stripes = btrfs_chunk_num_stripes(leaf, chunk); num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
/* Validation check */ sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
type = btrfs_chunk_type(leaf, chunk);
if (!num_stripes) { if (!num_stripes) {
btrfs_err(root->fs_info, "invalid chunk num_stripes: %u", btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
num_stripes); num_stripes);
...@@ -6282,6 +6278,11 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, ...@@ -6282,6 +6278,11 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
"invalid chunk logical %llu", logical); "invalid chunk logical %llu", logical);
return -EIO; return -EIO;
} }
if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
btrfs_err(root->fs_info, "invalid chunk sectorsize %u",
btrfs_chunk_sector_size(leaf, chunk));
return -EIO;
}
if (!length || !IS_ALIGNED(length, root->sectorsize)) { if (!length || !IS_ALIGNED(length, root->sectorsize)) {
btrfs_err(root->fs_info, btrfs_err(root->fs_info,
"invalid chunk length %llu", length); "invalid chunk length %llu", length);
...@@ -6293,13 +6294,54 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, ...@@ -6293,13 +6294,54 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
return -EIO; return -EIO;
} }
if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) & if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
btrfs_chunk_type(leaf, chunk)) { type) {
btrfs_err(root->fs_info, "unrecognized chunk type: %llu", btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
~(BTRFS_BLOCK_GROUP_TYPE_MASK | ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
BTRFS_BLOCK_GROUP_PROFILE_MASK) & BTRFS_BLOCK_GROUP_PROFILE_MASK) &
btrfs_chunk_type(leaf, chunk)); btrfs_chunk_type(leaf, chunk));
return -EIO; return -EIO;
} }
if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) ||
(type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
(type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
(type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
(type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
num_stripes != 1)) {
btrfs_err(root->fs_info,
"invalid num_stripes:sub_stripes %u:%u for profile %llu",
num_stripes, sub_stripes,
type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
return -EIO;
}
return 0;
}
static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
struct extent_buffer *leaf,
struct btrfs_chunk *chunk)
{
struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
struct map_lookup *map;
struct extent_map *em;
u64 logical;
u64 length;
u64 stripe_len;
u64 devid;
u8 uuid[BTRFS_UUID_SIZE];
int num_stripes;
int ret;
int i;
logical = key->offset;
length = btrfs_chunk_length(leaf, chunk);
stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
ret = btrfs_check_chunk_valid(root, leaf, chunk, logical);
if (ret)
return ret;
read_lock(&map_tree->map_tree.lock); read_lock(&map_tree->map_tree.lock);
em = lookup_extent_mapping(&map_tree->map_tree, logical, 1); em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
...@@ -6547,6 +6589,7 @@ int btrfs_read_sys_array(struct btrfs_root *root) ...@@ -6547,6 +6589,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
u32 array_size; u32 array_size;
u32 len = 0; u32 len = 0;
u32 cur_offset; u32 cur_offset;
u64 type;
struct btrfs_key key; struct btrfs_key key;
ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize); ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize);
...@@ -6613,6 +6656,15 @@ int btrfs_read_sys_array(struct btrfs_root *root) ...@@ -6613,6 +6656,15 @@ int btrfs_read_sys_array(struct btrfs_root *root)
break; break;
} }
type = btrfs_chunk_type(sb, chunk);
if ((type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
btrfs_err(root->fs_info,
"invalid chunk type %llu in sys_array at offset %u",
type, cur_offset);
ret = -EIO;
break;
}
len = btrfs_chunk_item_size(num_stripes); len = btrfs_chunk_item_size(num_stripes);
if (cur_offset + len > array_size) if (cur_offset + len > array_size)
goto out_short_read; goto out_short_read;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment