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>
This commit is contained in:
parent
a26fb01c28
commit
01cfb7937a
@ -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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
Loading…
Reference in New Issue
Block a user