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)) { |
