diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-07-29 15:44:46 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-07-29 15:44:46 -0400 |
commit | 01cfb7937a9af2abb1136c7e89fbf3fd92952956 (patch) | |
tree | 2b01fbc7eb315150d5a0ed71a218e4008801138b | |
parent | a26fb01c2879ed7026e6cbd78bb701912d249eef (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.c | 3 | ||||
-rw-r--r-- | fs/squashfs/file.c | 8 | ||||
-rw-r--r-- | fs/squashfs/fragment.c | 4 | ||||
-rw-r--r-- | fs/squashfs/squashfs_fs.h | 6 |
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 | ||
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 | * 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 |