squashfs: be more careful about metadata corruption

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>

Changed files
+16 -5
fs
+3
fs/squashfs/cache.c
··· 350 350 351 351 TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset); 352 352 353 + if (unlikely(length < 0)) 354 + return -EIO; 355 + 353 356 while (length) { 354 357 entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0); 355 358 if (entry->error) {
+6 -2
fs/squashfs/file.c
··· 194 194 } 195 195 196 196 for (i = 0; i < blocks; i++) { 197 - int size = le32_to_cpu(blist[i]); 197 + int size = squashfs_block_size(blist[i]); 198 + if (size < 0) { 199 + err = size; 200 + goto failure; 201 + } 198 202 block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size); 199 203 } 200 204 n -= blocks; ··· 371 367 sizeof(size)); 372 368 if (res < 0) 373 369 return res; 374 - return le32_to_cpu(size); 370 + return squashfs_block_size(size); 375 371 } 376 372 377 373 /* Copy data into page cache */
+1 -3
fs/squashfs/fragment.c
··· 61 61 return size; 62 62 63 63 *fragment_block = le64_to_cpu(fragment_entry.start_block); 64 - size = le32_to_cpu(fragment_entry.size); 65 - 66 - return size; 64 + return squashfs_block_size(fragment_entry.size); 67 65 } 68 66 69 67
+6
fs/squashfs/squashfs_fs.h
··· 129 129 130 130 #define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) 131 131 132 + static inline int squashfs_block_size(__le32 raw) 133 + { 134 + u32 size = le32_to_cpu(raw); 135 + return (size >> 25) ? -EIO : size; 136 + } 137 + 132 138 /* 133 139 * Inode number ops. Inodes consist of a compressed block number, and an 134 140 * uncompressed offset within that block