Skip to content

Commit e06cd3d

Browse files
Liu Bokdave
authored andcommitted
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>
1 parent 99e3ecf commit e06cd3d

File tree

1 file changed

+67
-15
lines changed

1 file changed

+67
-15
lines changed

fs/btrfs/volumes.c

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6251,27 +6251,23 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info,
62516251
return dev;
62526252
}
62536253

6254-
static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6255-
struct extent_buffer *leaf,
6256-
struct btrfs_chunk *chunk)
6254+
/* Return -EIO if any error, otherwise return 0. */
6255+
static int btrfs_check_chunk_valid(struct btrfs_root *root,
6256+
struct extent_buffer *leaf,
6257+
struct btrfs_chunk *chunk, u64 logical)
62576258
{
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;
62626259
u64 length;
62636260
u64 stripe_len;
6264-
u64 devid;
6265-
u8 uuid[BTRFS_UUID_SIZE];
6266-
int num_stripes;
6267-
int ret;
6268-
int i;
6261+
u16 num_stripes;
6262+
u16 sub_stripes;
6263+
u64 type;
62696264

6270-
logical = key->offset;
62716265
length = btrfs_chunk_length(leaf, chunk);
62726266
stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
62736267
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+
62756271
if (!num_stripes) {
62766272
btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
62776273
num_stripes);
@@ -6282,6 +6278,11 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
62826278
"invalid chunk logical %llu", logical);
62836279
return -EIO;
62846280
}
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+
}
62856286
if (!length || !IS_ALIGNED(length, root->sectorsize)) {
62866287
btrfs_err(root->fs_info,
62876288
"invalid chunk length %llu", length);
@@ -6293,13 +6294,54 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
62936294
return -EIO;
62946295
}
62956296
if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
6296-
btrfs_chunk_type(leaf, chunk)) {
6297+
type) {
62976298
btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
62986299
~(BTRFS_BLOCK_GROUP_TYPE_MASK |
62996300
BTRFS_BLOCK_GROUP_PROFILE_MASK) &
63006301
btrfs_chunk_type(leaf, chunk));
63016302
return -EIO;
63026303
}
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+
6321+
static 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;
63036345

63046346
read_lock(&map_tree->map_tree.lock);
63056347
em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
@@ -6547,6 +6589,7 @@ int btrfs_read_sys_array(struct btrfs_root *root)
65476589
u32 array_size;
65486590
u32 len = 0;
65496591
u32 cur_offset;
6592+
u64 type;
65506593
struct btrfs_key key;
65516594

65526595
ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize);
@@ -6613,6 +6656,15 @@ int btrfs_read_sys_array(struct btrfs_root *root)
66136656
break;
66146657
}
66156658

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+
66166668
len = btrfs_chunk_item_size(num_stripes);
66176669
if (cur_offset + len > array_size)
66186670
goto out_short_read;

0 commit comments

Comments
 (0)