summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-checker.c
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2018-07-03 05:10:05 -0400
committerDavid Sterba <dsterba@suse.com>2018-08-06 07:12:41 -0400
commitfce466eab7ac6baa9d2dcd88abcf945be3d4a089 (patch)
treeb1dfc302a5c93cf38ab82bf11cdabc2b47dc9f91 /fs/btrfs/tree-checker.c
parent6d8ff4e4584cd84093b751671351c8103ead551d (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.c100
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
359static 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
381static 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}