| From: Qu Wenruo <quwenruo@cn.fujitsu.com> |
| Date: Tue, 15 Dec 2015 09:14:37 +0800 |
| Subject: btrfs: Enhance chunk validation check |
| |
| commit f04b772bfc17f502703794f4d100d12155c1a1a9 upstream. |
| |
| Enhance chunk validation: |
| 1) Num_stripes |
| We already have such check but it's only in super block sys chunk |
| array. |
| Now check all on-disk chunks. |
| |
| 2) Chunk logical |
| It should be aligned to sector size. |
| This behavior should be *DOUBLE CHECKED* for 64K sector size like |
| PPC64 or AArch64. |
| Maybe we can found some hidden bugs. |
| |
| 3) Chunk length |
| Same as chunk logical, should be aligned to sector size. |
| |
| 4) Stripe length |
| It should be power of 2. |
| |
| 5) Chunk type |
| Any bit out of TYPE_MAS | PROFILE_MASK is invalid. |
| |
| With all these much restrict rules, several fuzzed image reported in |
| mail list should no longer cause kernel panic. |
| |
| Reported-by: Vegard Nossum <vegard.nossum@oracle.com> |
| Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> |
| Signed-off-by: Chris Mason <clm@fb.com> |
| Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/btrfs/volumes.c | 33 ++++++++++++++++++++++++++++++++- |
| 1 file changed, 32 insertions(+), 1 deletion(-) |
| |
| --- a/fs/btrfs/volumes.c |
| +++ b/fs/btrfs/volumes.c |
| @@ -5814,6 +5814,7 @@ static int read_one_chunk(struct btrfs_r |
| struct extent_map *em; |
| u64 logical; |
| u64 length; |
| + u64 stripe_len; |
| u64 devid; |
| u8 uuid[BTRFS_UUID_SIZE]; |
| int num_stripes; |
| @@ -5822,6 +5823,37 @@ static int read_one_chunk(struct btrfs_r |
| |
| 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); |
| + /* Validation check */ |
| + if (!num_stripes) { |
| + btrfs_err(root->fs_info, "invalid chunk num_stripes: %u", |
| + num_stripes); |
| + return -EIO; |
| + } |
| + if (!IS_ALIGNED(logical, root->sectorsize)) { |
| + btrfs_err(root->fs_info, |
| + "invalid chunk logical %llu", logical); |
| + return -EIO; |
| + } |
| + if (!length || !IS_ALIGNED(length, root->sectorsize)) { |
| + btrfs_err(root->fs_info, |
| + "invalid chunk length %llu", length); |
| + return -EIO; |
| + } |
| + if (!is_power_of_2(stripe_len)) { |
| + btrfs_err(root->fs_info, "invalid chunk stripe length: %llu", |
| + stripe_len); |
| + return -EIO; |
| + } |
| + if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) & |
| + btrfs_chunk_type(leaf, chunk)) { |
| + btrfs_err(root->fs_info, "unrecognized chunk type: %llu", |
| + ~(BTRFS_BLOCK_GROUP_TYPE_MASK | |
| + BTRFS_BLOCK_GROUP_PROFILE_MASK) & |
| + btrfs_chunk_type(leaf, chunk)); |
| + return -EIO; |
| + } |
| |
| read_lock(&map_tree->map_tree.lock); |
| em = lookup_extent_mapping(&map_tree->map_tree, logical, 1); |
| @@ -5838,7 +5870,6 @@ static int read_one_chunk(struct btrfs_r |
| em = alloc_extent_map(); |
| if (!em) |
| return -ENOMEM; |
| - num_stripes = btrfs_chunk_num_stripes(leaf, chunk); |
| map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); |
| if (!map) { |
| free_extent_map(em); |