aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4/extents.c
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2009-03-12 09:51:20 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-03-12 09:51:20 -0400
commit56b19868aca856a7d7bf20c3a7a1030e4fd75b2b (patch)
treeca8f9765aa6a319bbe2d5c014b0e27a949c4fc5d /fs/ext4/extents.c
parente6f009b0b45220c004672d41a58865e94946104d (diff)
ext4: Add checks to validate extent entries.
This patch adds checks to validate the extent entries along with extent headers, to avoid crashes caused by corrupt filesystems. Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4/extents.c')
-rw-r--r--fs/ext4/extents.c81
1 files changed, 71 insertions, 10 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index aa3431856c9a..ee40b7c52c8c 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -324,7 +324,64 @@ ext4_ext_max_entries(struct inode *inode, int depth)
324 return max; 324 return max;
325} 325}
326 326
327static int __ext4_ext_check_header(const char *function, struct inode *inode, 327static int ext4_valid_extent(struct inode *inode, struct ext4_extent *ext)
328{
329 ext4_fsblk_t block = ext_pblock(ext);
330 int len = ext4_ext_get_actual_len(ext);
331 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
332 if (unlikely(block < le32_to_cpu(es->s_first_data_block) ||
333 ((block + len) > ext4_blocks_count(es))))
334 return 0;
335 else
336 return 1;
337}
338
339static int ext4_valid_extent_idx(struct inode *inode,
340 struct ext4_extent_idx *ext_idx)
341{
342 ext4_fsblk_t block = idx_pblock(ext_idx);
343 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
344 if (unlikely(block < le32_to_cpu(es->s_first_data_block) ||
345 (block > ext4_blocks_count(es))))
346 return 0;
347 else
348 return 1;
349}
350
351static int ext4_valid_extent_entries(struct inode *inode,
352 struct ext4_extent_header *eh,
353 int depth)
354{
355 struct ext4_extent *ext;
356 struct ext4_extent_idx *ext_idx;
357 unsigned short entries;
358 if (eh->eh_entries == 0)
359 return 1;
360
361 entries = le16_to_cpu(eh->eh_entries);
362
363 if (depth == 0) {
364 /* leaf entries */
365 ext = EXT_FIRST_EXTENT(eh);
366 while (entries) {
367 if (!ext4_valid_extent(inode, ext))
368 return 0;
369 ext++;
370 entries--;
371 }
372 } else {
373 ext_idx = EXT_FIRST_INDEX(eh);
374 while (entries) {
375 if (!ext4_valid_extent_idx(inode, ext_idx))
376 return 0;
377 ext_idx++;
378 entries--;
379 }
380 }
381 return 1;
382}
383
384static int __ext4_ext_check(const char *function, struct inode *inode,
328 struct ext4_extent_header *eh, 385 struct ext4_extent_header *eh,
329 int depth) 386 int depth)
330{ 387{
@@ -352,11 +409,15 @@ static int __ext4_ext_check_header(const char *function, struct inode *inode,
352 error_msg = "invalid eh_entries"; 409 error_msg = "invalid eh_entries";
353 goto corrupted; 410 goto corrupted;
354 } 411 }
412 if (!ext4_valid_extent_entries(inode, eh, depth)) {
413 error_msg = "invalid extent entries";
414 goto corrupted;
415 }
355 return 0; 416 return 0;
356 417
357corrupted: 418corrupted:
358 ext4_error(inode->i_sb, function, 419 ext4_error(inode->i_sb, function,
359 "bad header in inode #%lu: %s - magic %x, " 420 "bad header/extent in inode #%lu: %s - magic %x, "
360 "entries %u, max %u(%u), depth %u(%u)", 421 "entries %u, max %u(%u), depth %u(%u)",
361 inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic), 422 inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic),
362 le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max), 423 le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max),
@@ -365,8 +426,8 @@ corrupted:
365 return -EIO; 426 return -EIO;
366} 427}
367 428
368#define ext4_ext_check_header(inode, eh, depth) \ 429#define ext4_ext_check(inode, eh, depth) \
369 __ext4_ext_check_header(__func__, inode, eh, depth) 430 __ext4_ext_check(__func__, inode, eh, depth)
370 431
371#ifdef EXT_DEBUG 432#ifdef EXT_DEBUG
372static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) 433static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path)
@@ -570,7 +631,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
570 631
571 eh = ext_inode_hdr(inode); 632 eh = ext_inode_hdr(inode);
572 depth = ext_depth(inode); 633 depth = ext_depth(inode);
573 if (ext4_ext_check_header(inode, eh, depth)) 634 if (ext4_ext_check(inode, eh, depth))
574 return ERR_PTR(-EIO); 635 return ERR_PTR(-EIO);
575 636
576 637
@@ -607,7 +668,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block,
607 path[ppos].p_hdr = eh; 668 path[ppos].p_hdr = eh;
608 i--; 669 i--;
609 670
610 if (ext4_ext_check_header(inode, eh, i)) 671 if (ext4_ext_check(inode, eh, i))
611 goto err; 672 goto err;
612 } 673 }
613 674
@@ -1204,7 +1265,7 @@ got_index:
1204 return -EIO; 1265 return -EIO;
1205 eh = ext_block_hdr(bh); 1266 eh = ext_block_hdr(bh);
1206 /* subtract from p_depth to get proper eh_depth */ 1267 /* subtract from p_depth to get proper eh_depth */
1207 if (ext4_ext_check_header(inode, eh, path->p_depth - depth)) { 1268 if (ext4_ext_check(inode, eh, path->p_depth - depth)) {
1208 put_bh(bh); 1269 put_bh(bh);
1209 return -EIO; 1270 return -EIO;
1210 } 1271 }
@@ -1217,7 +1278,7 @@ got_index:
1217 if (bh == NULL) 1278 if (bh == NULL)
1218 return -EIO; 1279 return -EIO;
1219 eh = ext_block_hdr(bh); 1280 eh = ext_block_hdr(bh);
1220 if (ext4_ext_check_header(inode, eh, path->p_depth - depth)) { 1281 if (ext4_ext_check(inode, eh, path->p_depth - depth)) {
1221 put_bh(bh); 1282 put_bh(bh);
1222 return -EIO; 1283 return -EIO;
1223 } 1284 }
@@ -2160,7 +2221,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
2160 return -ENOMEM; 2221 return -ENOMEM;
2161 } 2222 }
2162 path[0].p_hdr = ext_inode_hdr(inode); 2223 path[0].p_hdr = ext_inode_hdr(inode);
2163 if (ext4_ext_check_header(inode, path[0].p_hdr, depth)) { 2224 if (ext4_ext_check(inode, path[0].p_hdr, depth)) {
2164 err = -EIO; 2225 err = -EIO;
2165 goto out; 2226 goto out;
2166 } 2227 }
@@ -2214,7 +2275,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
2214 err = -EIO; 2275 err = -EIO;
2215 break; 2276 break;
2216 } 2277 }
2217 if (ext4_ext_check_header(inode, ext_block_hdr(bh), 2278 if (ext4_ext_check(inode, ext_block_hdr(bh),
2218 depth - i - 1)) { 2279 depth - i - 1)) {
2219 err = -EIO; 2280 err = -EIO;
2220 break; 2281 break;