diff options
-rw-r--r-- | fs/ext4/inode.c | 59 |
1 files changed, 52 insertions, 7 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c7d9250fbc3f..b3fd65d46506 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -371,6 +371,34 @@ static int ext4_block_to_path(struct inode *inode, | |||
371 | return n; | 371 | return n; |
372 | } | 372 | } |
373 | 373 | ||
374 | static int __ext4_check_blockref(const char *function, struct inode *inode, | ||
375 | unsigned int *p, unsigned int max) { | ||
376 | |||
377 | unsigned int maxblocks = ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es); | ||
378 | unsigned int *bref = p; | ||
379 | while (bref < p+max) { | ||
380 | if (unlikely(*bref >= maxblocks)) { | ||
381 | ext4_error(inode->i_sb, function, | ||
382 | "block reference %u >= max (%u) " | ||
383 | "in inode #%lu, offset=%d", | ||
384 | *bref, maxblocks, | ||
385 | inode->i_ino, (int)(bref-p)); | ||
386 | return -EIO; | ||
387 | } | ||
388 | bref++; | ||
389 | } | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | |||
394 | #define ext4_check_indirect_blockref(inode, bh) \ | ||
395 | __ext4_check_blockref(__func__, inode, (__le32 *)(bh)->b_data, \ | ||
396 | EXT4_ADDR_PER_BLOCK((inode)->i_sb)) | ||
397 | |||
398 | #define ext4_check_inode_blockref(inode) \ | ||
399 | __ext4_check_blockref(__func__, inode, EXT4_I(inode)->i_data, \ | ||
400 | EXT4_NDIR_BLOCKS) | ||
401 | |||
374 | /** | 402 | /** |
375 | * ext4_get_branch - read the chain of indirect blocks leading to data | 403 | * ext4_get_branch - read the chain of indirect blocks leading to data |
376 | * @inode: inode in question | 404 | * @inode: inode in question |
@@ -415,9 +443,22 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth, | |||
415 | if (!p->key) | 443 | if (!p->key) |
416 | goto no_block; | 444 | goto no_block; |
417 | while (--depth) { | 445 | while (--depth) { |
418 | bh = sb_bread(sb, le32_to_cpu(p->key)); | 446 | bh = sb_getblk(sb, le32_to_cpu(p->key)); |
419 | if (!bh) | 447 | if (unlikely(!bh)) |
420 | goto failure; | 448 | goto failure; |
449 | |||
450 | if (!bh_uptodate_or_lock(bh)) { | ||
451 | if (bh_submit_read(bh) < 0) { | ||
452 | put_bh(bh); | ||
453 | goto failure; | ||
454 | } | ||
455 | /* validate block references */ | ||
456 | if (ext4_check_indirect_blockref(inode, bh)) { | ||
457 | put_bh(bh); | ||
458 | goto failure; | ||
459 | } | ||
460 | } | ||
461 | |||
421 | add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets); | 462 | add_chain(++p, bh, (__le32 *)bh->b_data + *++offsets); |
422 | /* Reader: end */ | 463 | /* Reader: end */ |
423 | if (!p->key) | 464 | if (!p->key) |
@@ -4371,11 +4412,15 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) | |||
4371 | if (ei->i_flags & EXT4_EXTENTS_FL) { | 4412 | if (ei->i_flags & EXT4_EXTENTS_FL) { |
4372 | /* Validate extent which is part of inode */ | 4413 | /* Validate extent which is part of inode */ |
4373 | ret = ext4_ext_check_inode(inode); | 4414 | ret = ext4_ext_check_inode(inode); |
4374 | if (ret) { | 4415 | } else if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || |
4375 | brelse(bh); | 4416 | (S_ISLNK(inode->i_mode) && |
4376 | goto bad_inode; | 4417 | !ext4_inode_is_fast_symlink(inode))) { |
4377 | } | 4418 | /* Validate block references which are part of inode */ |
4378 | 4419 | ret = ext4_check_inode_blockref(inode); | |
4420 | } | ||
4421 | if (ret) { | ||
4422 | brelse(bh); | ||
4423 | goto bad_inode; | ||
4379 | } | 4424 | } |
4380 | 4425 | ||
4381 | if (S_ISREG(inode->i_mode)) { | 4426 | if (S_ISREG(inode->i_mode)) { |