aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Bo <bo.li.liu@oracle.com>2016-06-03 15:05:15 -0400
committerDavid Sterba <dsterba@suse.com>2016-06-06 04:57:09 -0400
commite06cd3dd7cea50e87663a88acdfdb7ac1c53a5ca (patch)
treede24cda9966c078446f925c2cd02b60ee7cbeb4c
parent99e3ecfcb9f4ca35192d20a5bea158b81f600062 (diff)
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: Vegard Nossum <vegard.nossum@oracle.com> Reported-by: Quentin Casasnovas <quentin.casasnovas@oracle.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/volumes.c82
1 files changed, 67 insertions, 15 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index fd5c9e69894a..74507b05061b 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6251,27 +6251,23 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
6251 return dev; 6251 return dev;
6252} 6252}
6253 6253
6254static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, 6254/* Return -EIO if any error, otherwise return 0. */
6255 struct extent_buffer *leaf, 6255static int btrfs_check_chunk_valid(struct btrfs_root *root,
6256 struct btrfs_chunk *chunk) 6256 struct extent_buffer *leaf,
6257 struct btrfs_chunk *chunk, u64 logical)
6257{ 6258{
6258 struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
6259 struct map_lookup *map;
6260 struct extent_map *em;
6261 u64 logical;
6262 u64 length; 6259 u64 length;
6263 u64 stripe_len; 6260 u64 stripe_len;
6264 u64 devid; 6261 u16 num_stripes;
6265 u8 uuid[BTRFS_UUID_SIZE]; 6262 u16 sub_stripes;
6266 int num_stripes; 6263 u64 type;
6267 int ret;
6268 int i;
6269 6264
6270 logical = key->offset;
6271 length = btrfs_chunk_length(leaf, chunk); 6265 length = btrfs_chunk_length(leaf, chunk);
6272 stripe_len = btrfs_chunk_stripe_len(leaf, chunk); 6266 stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
6273 num_stripes = btrfs_chunk_num_stripes(leaf, chunk); 6267 num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
6274 /* Validation check */ 6268 sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
6269 type = btrfs_chunk_type(leaf, chunk);
6270
6275 if (!num_stripes) { 6271 if (!num_stripes) {
6276 btrfs_err(root->fs_info, "invalid chunk num_stripes: %u", 6272 btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
6277 num_stripes); 6273 num_stripes);
@@ -6282,6 +6278,11 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6282 "invalid chunk logical %llu", logical); 6278 "invalid chunk logical %llu", logical);
6283 return -EIO; 6279 return -EIO;
6284 } 6280 }
6281 if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) {
6282 btrfs_err(root->fs_info, "invalid chunk sectorsize %u",
6283 btrfs_chunk_sector_size(leaf, chunk));
6284 return -EIO;
6285 }
6285 if (!length || !IS_ALIGNED(length, root->sectorsize)) { 6286 if (!length || !IS_ALIGNED(length, root->sectorsize)) {
6286 btrfs_err(root->fs_info, 6287 btrfs_err(root->fs_info,
6287 "invalid chunk length %llu", length); 6288 "invalid chunk length %llu", length);
@@ -6293,13 +6294,54 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6293 return -EIO; 6294 return -EIO;
6294 } 6295 }
6295 if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) & 6296 if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
6296 btrfs_chunk_type(leaf, chunk)) { 6297 type) {
6297 btrfs_err(root->fs_info, "unrecognized chunk type: %llu", 6298 btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
6298 ~(BTRFS_BLOCK_GROUP_TYPE_MASK | 6299 ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
6299 BTRFS_BLOCK_GROUP_PROFILE_MASK) & 6300 BTRFS_BLOCK_GROUP_PROFILE_MASK) &
6300 btrfs_chunk_type(leaf, chunk)); 6301 btrfs_chunk_type(leaf, chunk));
6301 return -EIO; 6302 return -EIO;
6302 } 6303 }
6304 if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) ||
6305 (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) ||
6306 (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
6307 (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
6308 (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) ||
6309 ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 &&
6310 num_stripes != 1)) {
6311 btrfs_err(root->fs_info,
6312 "invalid num_stripes:sub_stripes %u:%u for profile %llu",
6313 num_stripes, sub_stripes,
6314 type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
6315 return -EIO;
6316 }
6317
6318 return 0;
6319}
6320
6321static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6322 struct extent_buffer *leaf,
6323 struct btrfs_chunk *chunk)
6324{
6325 struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
6326 struct map_lookup *map;
6327 struct extent_map *em;
6328 u64 logical;
6329 u64 length;
6330 u64 stripe_len;
6331 u64 devid;
6332 u8 uuid[BTRFS_UUID_SIZE];
6333 int num_stripes;
6334 int ret;
6335 int i;
6336
6337 logical = key->offset;
6338 length = btrfs_chunk_length(leaf, chunk);
6339 stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
6340 num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
6341
6342 ret = btrfs_check_chunk_valid(root, leaf, chunk, logical);
6343 if (ret)
6344 return ret;
6303 6345
6304 read_lock(&map_tree->map_tree.lock); 6346 read_lock(&map_tree->map_tree.lock);
6305 em = lookup_extent_mapping(&map_tree->map_tree, logical, 1); 6347 em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
@@ -6547,6 +6589,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
6547 u32 array_size; 6589 u32 array_size;
6548 u32 len = 0; 6590 u32 len = 0;
6549 u32 cur_offset; 6591 u32 cur_offset;
6592 u64 type;
6550 struct btrfs_key key; 6593 struct btrfs_key key;
6551 6594
6552 ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize); 6595 ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize);
@@ -6613,6 +6656,15 @@ int btrfs_read_sys_array(struct btrfs_root *root)
6613 break; 6656 break;
6614 } 6657 }
6615 6658
6659 type = btrfs_chunk_type(sb, chunk);
6660 if ((type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) {
6661 btrfs_err(root->fs_info,
6662 "invalid chunk type %llu in sys_array at offset %u",
6663 type, cur_offset);
6664 ret = -EIO;
6665 break;
6666 }
6667
6616 len = btrfs_chunk_item_size(num_stripes); 6668 len = btrfs_chunk_item_size(num_stripes);
6617 if (cur_offset + len > array_size) 6669 if (cur_offset + len > array_size)
6618 goto out_short_read; 6670 goto out_short_read;