diff options
author | Qu Wenruo <wqu@suse.com> | 2018-07-03 05:10:05 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2018-08-06 07:12:41 -0400 |
commit | fce466eab7ac6baa9d2dcd88abcf945be3d4a089 (patch) | |
tree | b1dfc302a5c93cf38ab82bf11cdabc2b47dc9f91 /fs/btrfs/tree-checker.c | |
parent | 6d8ff4e4584cd84093b751671351c8103ead551d (diff) |
btrfs: tree-checker: Verify block_group_item
A crafted image with invalid block group items could make free space cache
code to cause panic.
We could detect such invalid block group item by checking:
1) Item size
Known fixed value.
2) Block group size (key.offset)
We have an upper limit on block group item (10G)
3) Chunk objectid
Known fixed value.
4) Type
Only 4 valid type values, DATA, METADATA, SYSTEM and DATA|METADATA.
No more than 1 bit set for profile type.
5) Used space
No more than the block group size.
This should allow btrfs to detect and refuse to mount the crafted image.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=199849
Reported-by: Xu Wen <wen.xu@gatech.edu>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Tested-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/tree-checker.c')
-rw-r--r-- | fs/btrfs/tree-checker.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 8d40e7dd8c30..3bade2ad39e8 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include "tree-checker.h" | 19 | #include "tree-checker.h" |
20 | #include "disk-io.h" | 20 | #include "disk-io.h" |
21 | #include "compression.h" | 21 | #include "compression.h" |
22 | #include "volumes.h" | ||
22 | 23 | ||
23 | /* | 24 | /* |
24 | * Error message should follow the following format: | 25 | * Error message should follow the following format: |
@@ -353,6 +354,102 @@ static int check_dir_item(struct btrfs_fs_info *fs_info, | |||
353 | return 0; | 354 | return 0; |
354 | } | 355 | } |
355 | 356 | ||
357 | __printf(4, 5) | ||
358 | __cold | ||
359 | static void block_group_err(const struct btrfs_fs_info *fs_info, | ||
360 | const struct extent_buffer *eb, int slot, | ||
361 | const char *fmt, ...) | ||
362 | { | ||
363 | struct btrfs_key key; | ||
364 | struct va_format vaf; | ||
365 | va_list args; | ||
366 | |||
367 | btrfs_item_key_to_cpu(eb, &key, slot); | ||
368 | va_start(args, fmt); | ||
369 | |||
370 | vaf.fmt = fmt; | ||
371 | vaf.va = &args; | ||
372 | |||
373 | btrfs_crit(fs_info, | ||
374 | "corrupt %s: root=%llu block=%llu slot=%d bg_start=%llu bg_len=%llu, %pV", | ||
375 | btrfs_header_level(eb) == 0 ? "leaf" : "node", | ||
376 | btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, | ||
377 | key.objectid, key.offset, &vaf); | ||
378 | va_end(args); | ||
379 | } | ||
380 | |||
381 | static int check_block_group_item(struct btrfs_fs_info *fs_info, | ||
382 | struct extent_buffer *leaf, | ||
383 | struct btrfs_key *key, int slot) | ||
384 | { | ||
385 | struct btrfs_block_group_item bgi; | ||
386 | u32 item_size = btrfs_item_size_nr(leaf, slot); | ||
387 | u64 flags; | ||
388 | u64 type; | ||
389 | |||
390 | /* | ||
391 | * Here we don't really care about alignment since extent allocator can | ||
392 | * handle it. We care more about the size, as if one block group is | ||
393 | * larger than maximum size, it's must be some obvious corruption. | ||
394 | */ | ||
395 | if (key->offset > BTRFS_MAX_DATA_CHUNK_SIZE || key->offset == 0) { | ||
396 | block_group_err(fs_info, leaf, slot, | ||
397 | "invalid block group size, have %llu expect (0, %llu]", | ||
398 | key->offset, BTRFS_MAX_DATA_CHUNK_SIZE); | ||
399 | return -EUCLEAN; | ||
400 | } | ||
401 | |||
402 | if (item_size != sizeof(bgi)) { | ||
403 | block_group_err(fs_info, leaf, slot, | ||
404 | "invalid item size, have %u expect %zu", | ||
405 | item_size, sizeof(bgi)); | ||
406 | return -EUCLEAN; | ||
407 | } | ||
408 | |||
409 | read_extent_buffer(leaf, &bgi, btrfs_item_ptr_offset(leaf, slot), | ||
410 | sizeof(bgi)); | ||
411 | if (btrfs_block_group_chunk_objectid(&bgi) != | ||
412 | BTRFS_FIRST_CHUNK_TREE_OBJECTID) { | ||
413 | block_group_err(fs_info, leaf, slot, | ||
414 | "invalid block group chunk objectid, have %llu expect %llu", | ||
415 | btrfs_block_group_chunk_objectid(&bgi), | ||
416 | BTRFS_FIRST_CHUNK_TREE_OBJECTID); | ||
417 | return -EUCLEAN; | ||
418 | } | ||
419 | |||
420 | if (btrfs_block_group_used(&bgi) > key->offset) { | ||
421 | block_group_err(fs_info, leaf, slot, | ||
422 | "invalid block group used, have %llu expect [0, %llu)", | ||
423 | btrfs_block_group_used(&bgi), key->offset); | ||
424 | return -EUCLEAN; | ||
425 | } | ||
426 | |||
427 | flags = btrfs_block_group_flags(&bgi); | ||
428 | if (hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) > 1) { | ||
429 | block_group_err(fs_info, leaf, slot, | ||
430 | "invalid profile flags, have 0x%llx (%lu bits set) expect no more than 1 bit set", | ||
431 | flags & BTRFS_BLOCK_GROUP_PROFILE_MASK, | ||
432 | hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK)); | ||
433 | return -EUCLEAN; | ||
434 | } | ||
435 | |||
436 | type = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; | ||
437 | if (type != BTRFS_BLOCK_GROUP_DATA && | ||
438 | type != BTRFS_BLOCK_GROUP_METADATA && | ||
439 | type != BTRFS_BLOCK_GROUP_SYSTEM && | ||
440 | type != (BTRFS_BLOCK_GROUP_METADATA | | ||
441 | BTRFS_BLOCK_GROUP_DATA)) { | ||
442 | block_group_err(fs_info, leaf, slot, | ||
443 | "invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llu or 0x%llx", | ||
444 | type, hweight64(type), | ||
445 | BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA, | ||
446 | BTRFS_BLOCK_GROUP_SYSTEM, | ||
447 | BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA); | ||
448 | return -EUCLEAN; | ||
449 | } | ||
450 | return 0; | ||
451 | } | ||
452 | |||
356 | /* | 453 | /* |
357 | * Common point to switch the item-specific validation. | 454 | * Common point to switch the item-specific validation. |
358 | */ | 455 | */ |
@@ -374,6 +471,9 @@ static int check_leaf_item(struct btrfs_fs_info *fs_info, | |||
374 | case BTRFS_XATTR_ITEM_KEY: | 471 | case BTRFS_XATTR_ITEM_KEY: |
375 | ret = check_dir_item(fs_info, leaf, key, slot); | 472 | ret = check_dir_item(fs_info, leaf, key, slot); |
376 | break; | 473 | break; |
474 | case BTRFS_BLOCK_GROUP_ITEM_KEY: | ||
475 | ret = check_block_group_item(fs_info, leaf, key, slot); | ||
476 | break; | ||
377 | } | 477 | } |
378 | return ret; | 478 | return ret; |
379 | } | 479 | } |