aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorQu Wenruo <quwenruo@cn.fujitsu.com>2015-12-14 20:14:37 -0500
committerChris Mason <clm@fb.com>2016-01-19 21:21:41 -0500
commitf04b772bfc17f502703794f4d100d12155c1a1a9 (patch)
treef69d161dd9d146284d0e0870fcfce492ea210a6a /fs
parent319e4d0661e5323c9f9945f0f8fb5905e5fe74c3 (diff)
btrfs: Enhance chunk validation check
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/volumes.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 3d0122d1a49c..91bd948d600b 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6211,6 +6211,7 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6211 struct extent_map *em; 6211 struct extent_map *em;
6212 u64 logical; 6212 u64 logical;
6213 u64 length; 6213 u64 length;
6214 u64 stripe_len;
6214 u64 devid; 6215 u64 devid;
6215 u8 uuid[BTRFS_UUID_SIZE]; 6216 u8 uuid[BTRFS_UUID_SIZE];
6216 int num_stripes; 6217 int num_stripes;
@@ -6219,6 +6220,37 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6219 6220
6220 logical = key->offset; 6221 logical = key->offset;
6221 length = btrfs_chunk_length(leaf, chunk); 6222 length = btrfs_chunk_length(leaf, chunk);
6223 stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
6224 num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
6225 /* Validation check */
6226 if (!num_stripes) {
6227 btrfs_err(root->fs_info, "invalid chunk num_stripes: %u",
6228 num_stripes);
6229 return -EIO;
6230 }
6231 if (!IS_ALIGNED(logical, root->sectorsize)) {
6232 btrfs_err(root->fs_info,
6233 "invalid chunk logical %llu", logical);
6234 return -EIO;
6235 }
6236 if (!length || !IS_ALIGNED(length, root->sectorsize)) {
6237 btrfs_err(root->fs_info,
6238 "invalid chunk length %llu", length);
6239 return -EIO;
6240 }
6241 if (!is_power_of_2(stripe_len)) {
6242 btrfs_err(root->fs_info, "invalid chunk stripe length: %llu",
6243 stripe_len);
6244 return -EIO;
6245 }
6246 if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
6247 btrfs_chunk_type(leaf, chunk)) {
6248 btrfs_err(root->fs_info, "unrecognized chunk type: %llu",
6249 ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
6250 BTRFS_BLOCK_GROUP_PROFILE_MASK) &
6251 btrfs_chunk_type(leaf, chunk));
6252 return -EIO;
6253 }
6222 6254
6223 read_lock(&map_tree->map_tree.lock); 6255 read_lock(&map_tree->map_tree.lock);
6224 em = lookup_extent_mapping(&map_tree->map_tree, logical, 1); 6256 em = lookup_extent_mapping(&map_tree->map_tree, logical, 1);
@@ -6235,7 +6267,6 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key,
6235 em = alloc_extent_map(); 6267 em = alloc_extent_map();
6236 if (!em) 6268 if (!em)
6237 return -ENOMEM; 6269 return -ENOMEM;
6238 num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
6239 map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS); 6270 map = kmalloc(map_lookup_size(num_stripes), GFP_NOFS);
6240 if (!map) { 6271 if (!map) {
6241 free_extent_map(em); 6272 free_extent_map(em);