This is a port of Linux commit 01cfb7937a9af2abb1136c7e89fbf3fd92952956: | Author: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> | AuthorDate: Sun Jul 29 12:44:46 2018 -0700 | | Anatoly Trosinenko reports that a corrupted squashfs image can cause a | kernel oops. It turns out that squashfs can end up being confused about | negative fragment lengths. | | The regular squashfs_read_data() does check for negative lengths, but | squashfs_read_metadata() did not, and the fragment size code just | blindly trusted the on-disk value. Fix both the fragment parsing and | the metadata reading code. | | Reported-by: Anatoly Trosinenko <anatoly.trosinenko@xxxxxxxxx> | Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx> | Cc: Phillip Lougher <phillip@xxxxxxxxxxxxxxx> | Cc: stable@xxxxxxxxxx | Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Reported-by: Richard Weinberger <richard@xxxxxxxxxxxxx> Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- fs/squashfs/cache.c | 3 +++ fs/squashfs/file.c | 8 ++++++-- fs/squashfs/fragment.c | 4 +--- fs/squashfs/squashfs_fs.h | 11 +++++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 766bc99493b9..5a027d6fe5d0 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -284,6 +284,9 @@ int squashfs_read_metadata(struct super_block *sb, void *buffer, TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset); + if (unlikely(length < 0)) + return -EIO; + while (length) { entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0); if (entry->error) { diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index 0806f90b9667..1413ef7ecbd8 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -169,7 +169,11 @@ static long long read_indexes(struct super_block *sb, int n, } for (i = 0; i < blocks; i++) { - int size = le32_to_cpu(blist[i]); + int size = squashfs_block_size(blist[i]); + if (size < 0) { + err = size; + goto failure; + } block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size); } n -= blocks; @@ -340,7 +344,7 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) sizeof(size)); if (res < 0) return res; - return le32_to_cpu(size); + return squashfs_block_size(size); } /* Copy data into page cache */ diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c index 2b99ff52e334..343444000e02 100644 --- a/fs/squashfs/fragment.c +++ b/fs/squashfs/fragment.c @@ -56,9 +56,7 @@ int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment, return size; *fragment_block = le64_to_cpu(fragment_entry.start_block); - size = le32_to_cpu(fragment_entry.size); - - return size; + return squashfs_block_size(fragment_entry.size); } diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h index 279a3db1bcb2..6ce6ed01ba76 100644 --- a/fs/squashfs/squashfs_fs.h +++ b/fs/squashfs/squashfs_fs.h @@ -1,5 +1,10 @@ #ifndef SQUASHFS_FS #define SQUASHFS_FS + +#include <asm/byteorder.h> +#include <linux/types.h> +#include <linux/errno.h> + /* * Squashfs * @@ -125,6 +130,12 @@ #define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) +static inline int squashfs_block_size(__le32 raw) +{ + u32 size = le32_to_cpu(raw); + return (size >> 25) ? -EIO : size; +} + /* * Inode number ops. Inodes consist of a compressed block number, and an * uncompressed offset within that block -- 2.39.2