提交 e06cd3dd 编写于 作者: L Liu Bo 提交者: 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: NVegard Nossum <vegard.nossum@oracle.com>
Reported-by: NQuentin Casasnovas <quentin.casasnovas@oracle.com>
Reviewed-by: NDavid Sterba <dsterba@suse.com>
Signed-off-by: NLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: NDavid Sterba <dsterba@suse.com>
上级 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. */
static int btrfs_check_chunk_valid(struct btrfs_root *root,
struct extent_buffer *leaf, struct extent_buffer *leaf,
struct btrfs_chunk *chunk) 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% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册