squashfs: be more careful about metadata corruption
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 29 Jul 2018 19:44:46 +0000 (12:44 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 3 Aug 2018 05:50:43 +0000 (07:50 +0200)
commit 01cfb7937a9af2abb1136c7e89fbf3fd92952956 upstream.

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@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Phillip Lougher <phillip@squashfs.org.uk>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/squashfs/cache.c
fs/squashfs/file.c
fs/squashfs/fragment.c
fs/squashfs/squashfs_fs.h

index 23813c078cc9527f547c345ba01ce31dafd570ab..0839efa720b3b562a9e56e677631fe9458ca9233 100644 (file)
@@ -350,6 +350,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) {
index 13d80947bf9e6adac348878e3494b38cdd206099..fcff2e0487fef11f89724a72221090e529f0a775 100644 (file)
@@ -194,7 +194,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;
@@ -367,7 +371,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  */
index 0ed6edbc5c7170aa06f191e33df193721206cb3f..86ad9a4b8c364d389df43f1fa6beb2e6b737e827 100644 (file)
@@ -61,9 +61,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);
 }
 
 
index 24d12fd1417767689778302abf77b21f1efd6350..4e6853f084d071b6291da9891b8a16c730901e48 100644 (file)
 
 #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