| From 71755ee5350b63fb1f283de8561cdb61b47f4d1d Mon Sep 17 00:00:00 2001 |
| From: Linus Torvalds <torvalds@linux-foundation.org> |
| Date: Thu, 2 Aug 2018 08:43:35 -0700 |
| Subject: squashfs: more metadata hardenings |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Linus Torvalds <torvalds@linux-foundation.org> |
| |
| commit 71755ee5350b63fb1f283de8561cdb61b47f4d1d upstream. |
| |
| The squashfs fragment reading code doesn't actually verify that the |
| fragment is inside the fragment table. The end result _is_ verified to |
| be inside the image when actually reading the fragment data, but before |
| that is done, we may end up taking a page fault because the fragment |
| table itself might not even exist. |
| |
| Another report from Anatoly and his endless squashfs image fuzzing. |
| |
| Reported-by: Анатолий Тросиненко <anatoly.trosinenko@gmail.com> |
| Acked-by:: Phillip Lougher <phillip.lougher@gmail.com>, |
| Cc: Willy Tarreau <w@1wt.eu> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/squashfs/fragment.c | 13 +++++++++---- |
| fs/squashfs/squashfs_fs_sb.h | 1 + |
| fs/squashfs/super.c | 5 +++-- |
| 3 files changed, 13 insertions(+), 6 deletions(-) |
| |
| --- a/fs/squashfs/fragment.c |
| +++ b/fs/squashfs/fragment.c |
| @@ -49,11 +49,16 @@ int squashfs_frag_lookup(struct super_bl |
| u64 *fragment_block) |
| { |
| struct squashfs_sb_info *msblk = sb->s_fs_info; |
| - int block = SQUASHFS_FRAGMENT_INDEX(fragment); |
| - int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); |
| - u64 start_block = le64_to_cpu(msblk->fragment_index[block]); |
| + int block, offset, size; |
| struct squashfs_fragment_entry fragment_entry; |
| - int size; |
| + u64 start_block; |
| + |
| + if (fragment >= msblk->fragments) |
| + return -EIO; |
| + block = SQUASHFS_FRAGMENT_INDEX(fragment); |
| + offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); |
| + |
| + start_block = le64_to_cpu(msblk->fragment_index[block]); |
| |
| size = squashfs_read_metadata(sb, &fragment_entry, &start_block, |
| &offset, sizeof(fragment_entry)); |
| --- a/fs/squashfs/squashfs_fs_sb.h |
| +++ b/fs/squashfs/squashfs_fs_sb.h |
| @@ -75,6 +75,7 @@ struct squashfs_sb_info { |
| unsigned short block_log; |
| long long bytes_used; |
| unsigned int inodes; |
| + unsigned int fragments; |
| int xattr_ids; |
| }; |
| #endif |
| --- a/fs/squashfs/super.c |
| +++ b/fs/squashfs/super.c |
| @@ -175,6 +175,7 @@ static int squashfs_fill_super(struct su |
| msblk->inode_table = le64_to_cpu(sblk->inode_table_start); |
| msblk->directory_table = le64_to_cpu(sblk->directory_table_start); |
| msblk->inodes = le32_to_cpu(sblk->inodes); |
| + msblk->fragments = le32_to_cpu(sblk->fragments); |
| flags = le16_to_cpu(sblk->flags); |
| |
| TRACE("Found valid superblock on %pg\n", sb->s_bdev); |
| @@ -185,7 +186,7 @@ static int squashfs_fill_super(struct su |
| TRACE("Filesystem size %lld bytes\n", msblk->bytes_used); |
| TRACE("Block size %d\n", msblk->block_size); |
| TRACE("Number of inodes %d\n", msblk->inodes); |
| - TRACE("Number of fragments %d\n", le32_to_cpu(sblk->fragments)); |
| + TRACE("Number of fragments %d\n", msblk->fragments); |
| TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids)); |
| TRACE("sblk->inode_table_start %llx\n", msblk->inode_table); |
| TRACE("sblk->directory_table_start %llx\n", msblk->directory_table); |
| @@ -272,7 +273,7 @@ allocate_id_index_table: |
| sb->s_export_op = &squashfs_export_ops; |
| |
| handle_fragments: |
| - fragments = le32_to_cpu(sblk->fragments); |
| + fragments = msblk->fragments; |
| if (fragments == 0) |
| goto check_directory_table; |
| |