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 |
