Commit 01cfb793 authored by Linus Torvalds's avatar Linus Torvalds

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: default avatarAnatoly 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: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent a26fb01c
...@@ -350,6 +350,9 @@ int squashfs_read_metadata(struct super_block *sb, void *buffer, ...@@ -350,6 +350,9 @@ int squashfs_read_metadata(struct super_block *sb, void *buffer,
TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset); TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);
if (unlikely(length < 0))
return -EIO;
while (length) { while (length) {
entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0); entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0);
if (entry->error) { if (entry->error) {
......
...@@ -194,7 +194,11 @@ static long long read_indexes(struct super_block *sb, int n, ...@@ -194,7 +194,11 @@ static long long read_indexes(struct super_block *sb, int n,
} }
for (i = 0; i < blocks; i++) { 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); block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
} }
n -= blocks; n -= blocks;
...@@ -367,7 +371,7 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) ...@@ -367,7 +371,7 @@ static int read_blocklist(struct inode *inode, int index, u64 *block)
sizeof(size)); sizeof(size));
if (res < 0) if (res < 0)
return res; return res;
return le32_to_cpu(size); return squashfs_block_size(size);
} }
/* Copy data into page cache */ /* Copy data into page cache */
......
...@@ -61,9 +61,7 @@ int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment, ...@@ -61,9 +61,7 @@ int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment,
return size; return size;
*fragment_block = le64_to_cpu(fragment_entry.start_block); *fragment_block = le64_to_cpu(fragment_entry.start_block);
size = le32_to_cpu(fragment_entry.size); return squashfs_block_size(fragment_entry.size);
return size;
} }
......
...@@ -129,6 +129,12 @@ ...@@ -129,6 +129,12 @@
#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) #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 * Inode number ops. Inodes consist of a compressed block number, and an
* uncompressed offset within that block * uncompressed offset within that block
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment