summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-07-29 15:44:46 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-07-29 15:44:46 -0400
commit01cfb7937a9af2abb1136c7e89fbf3fd92952956 (patch)
tree2b01fbc7eb315150d5a0ed71a218e4008801138b
parenta26fb01c2879ed7026e6cbd78bb701912d249eef (diff)
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>
-rw-r--r--fs/squashfs/cache.c3
-rw-r--r--fs/squashfs/file.c8
-rw-r--r--fs/squashfs/fragment.c4
-rw-r--r--fs/squashfs/squashfs_fs.h6
4 files changed, 16 insertions, 5 deletions
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 23813c078cc9..0839efa720b3 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -350,6 +350,9 @@ int squashfs_read_metadata(struct super_block *sb, void *buffer,
350 350
351 TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset); 351 TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);
352 352
353 if (unlikely(length < 0))
354 return -EIO;
355
353 while (length) { 356 while (length) {
354 entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0); 357 entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0);
355 if (entry->error) { 358 if (entry->error) {
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index 13d80947bf9e..fcff2e0487fe 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -194,7 +194,11 @@ static long long read_indexes(struct super_block *sb, int n,
194 } 194 }
195 195
196 for (i = 0; i < blocks; i++) { 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 block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size); 202 block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
199 } 203 }
200 n -= blocks; 204 n -= blocks;
@@ -367,7 +371,7 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
367 sizeof(size)); 371 sizeof(size));
368 if (res < 0) 372 if (res < 0)
369 return res; 373 return res;
370 return le32_to_cpu(size); 374 return squashfs_block_size(size);
371} 375}
372 376
373/* Copy data into page cache */ 377/* Copy data into page cache */
diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c
index 0ed6edbc5c71..86ad9a4b8c36 100644
--- a/fs/squashfs/fragment.c
+++ b/fs/squashfs/fragment.c
@@ -61,9 +61,7 @@ int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment,
61 return size; 61 return size;
62 62
63 *fragment_block = le64_to_cpu(fragment_entry.start_block); 63 *fragment_block = le64_to_cpu(fragment_entry.start_block);
64 size = le32_to_cpu(fragment_entry.size); 64 return squashfs_block_size(fragment_entry.size);
65
66 return size;
67} 65}
68 66
69 67
diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
index 24d12fd14177..4e6853f084d0 100644
--- a/fs/squashfs/squashfs_fs.h
+++ b/fs/squashfs/squashfs_fs.h
@@ -129,6 +129,12 @@
129 129
130#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) 130#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
131 131
132static 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 * Inode number ops. Inodes consist of a compressed block number, and an 139 * Inode number ops. Inodes consist of a compressed block number, and an
134 * uncompressed offset within that block 140 * uncompressed offset within that block