diff options
| author | Theodore Ts'o <tytso@mit.edu> | 2009-01-16 11:13:40 -0500 |
|---|---|---|
| committer | Theodore Ts'o <tytso@mit.edu> | 2009-01-16 11:13:40 -0500 |
| commit | e6b8bc09ba2075cd91fbffefcd2778b1a00bd76f (patch) | |
| tree | 07a439d21576794c5d39a06a86b09e690e55f2f2 | |
| parent | 06a279d636734da32bb62dd2f7b0ade666f65d7c (diff) | |
ext4: Add sanity check to make_indexed_dir
Make sure the rec_len field in the '..' entry is sane, lest we overrun
the directory block and cause a kernel oops on a purposefully
corrupted filesystem.
Thanks to Sami Liedes for reporting this bug.
http://bugzilla.kernel.org/show_bug.cgi?id=12430
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: stable@kernel.org
| -rw-r--r-- | fs/ext4/namei.c | 21 |
1 files changed, 15 insertions, 6 deletions
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index fec0b4c2f5f1..ba702bd7910d 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c | |||
| @@ -1368,7 +1368,7 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, | |||
| 1368 | struct fake_dirent *fde; | 1368 | struct fake_dirent *fde; |
| 1369 | 1369 | ||
| 1370 | blocksize = dir->i_sb->s_blocksize; | 1370 | blocksize = dir->i_sb->s_blocksize; |
| 1371 | dxtrace(printk(KERN_DEBUG "Creating index\n")); | 1371 | dxtrace(printk(KERN_DEBUG "Creating index: inode %lu\n", dir->i_ino)); |
| 1372 | retval = ext4_journal_get_write_access(handle, bh); | 1372 | retval = ext4_journal_get_write_access(handle, bh); |
| 1373 | if (retval) { | 1373 | if (retval) { |
| 1374 | ext4_std_error(dir->i_sb, retval); | 1374 | ext4_std_error(dir->i_sb, retval); |
| @@ -1377,6 +1377,20 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, | |||
| 1377 | } | 1377 | } |
| 1378 | root = (struct dx_root *) bh->b_data; | 1378 | root = (struct dx_root *) bh->b_data; |
| 1379 | 1379 | ||
| 1380 | /* The 0th block becomes the root, move the dirents out */ | ||
| 1381 | fde = &root->dotdot; | ||
| 1382 | de = (struct ext4_dir_entry_2 *)((char *)fde + | ||
| 1383 | ext4_rec_len_from_disk(fde->rec_len)); | ||
| 1384 | if ((char *) de >= (((char *) root) + blocksize)) { | ||
| 1385 | ext4_error(dir->i_sb, __func__, | ||
| 1386 | "invalid rec_len for '..' in inode %lu", | ||
| 1387 | dir->i_ino); | ||
| 1388 | brelse(bh); | ||
| 1389 | return -EIO; | ||
| 1390 | } | ||
| 1391 | len = ((char *) root) + blocksize - (char *) de; | ||
| 1392 | |||
| 1393 | /* Allocate new block for the 0th block's dirents */ | ||
| 1380 | bh2 = ext4_append(handle, dir, &block, &retval); | 1394 | bh2 = ext4_append(handle, dir, &block, &retval); |
| 1381 | if (!(bh2)) { | 1395 | if (!(bh2)) { |
| 1382 | brelse(bh); | 1396 | brelse(bh); |
| @@ -1385,11 +1399,6 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, | |||
| 1385 | EXT4_I(dir)->i_flags |= EXT4_INDEX_FL; | 1399 | EXT4_I(dir)->i_flags |= EXT4_INDEX_FL; |
| 1386 | data1 = bh2->b_data; | 1400 | data1 = bh2->b_data; |
| 1387 | 1401 | ||
| 1388 | /* The 0th block becomes the root, move the dirents out */ | ||
| 1389 | fde = &root->dotdot; | ||
| 1390 | de = (struct ext4_dir_entry_2 *)((char *)fde + | ||
| 1391 | ext4_rec_len_from_disk(fde->rec_len)); | ||
| 1392 | len = ((char *) root) + blocksize - (char *) de; | ||
| 1393 | memcpy (data1, de, len); | 1402 | memcpy (data1, de, len); |
| 1394 | de = (struct ext4_dir_entry_2 *) data1; | 1403 | de = (struct ext4_dir_entry_2 *) data1; |
| 1395 | top = data1 + len; | 1404 | top = data1 + len; |
