Squashfs: add sanity checks to lookup table reading at mount time
Fsfuzzer generates corrupted filesystems which throw a warn_on in kmalloc. One of these is due to a corrupted superblock inodes field. Fix this by checking that the number of bytes to be read (and allocated) does not extend into the next filesystem structure. Also add a couple of other sanity checks of the mount-time lookup table structures. Signed-off-by: Phillip Lougher <phillip@lougher.demon.co.uk>
This commit is contained in:
@@ -121,13 +121,38 @@ static struct dentry *squashfs_get_parent(struct dentry *child)
|
|||||||
* Read uncompressed inode lookup table indexes off disk into memory
|
* Read uncompressed inode lookup table indexes off disk into memory
|
||||||
*/
|
*/
|
||||||
__le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
|
__le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
|
||||||
u64 lookup_table_start, unsigned int inodes)
|
u64 lookup_table_start, u64 next_table, unsigned int inodes)
|
||||||
{
|
{
|
||||||
unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
|
unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
|
||||||
|
__le64 *table;
|
||||||
|
|
||||||
TRACE("In read_inode_lookup_table, length %d\n", length);
|
TRACE("In read_inode_lookup_table, length %d\n", length);
|
||||||
|
|
||||||
return squashfs_read_table(sb, lookup_table_start, length);
|
/* Sanity check values */
|
||||||
|
|
||||||
|
/* there should always be at least one inode */
|
||||||
|
if (inodes == 0)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
/* length bytes should not extend into the next table - this check
|
||||||
|
* also traps instances where lookup_table_start is incorrectly larger
|
||||||
|
* than the next table start
|
||||||
|
*/
|
||||||
|
if (lookup_table_start + length > next_table)
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
|
table = squashfs_read_table(sb, lookup_table_start, length);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* table[0] points to the first inode lookup table metadata block,
|
||||||
|
* this should be less than lookup_table_start
|
||||||
|
*/
|
||||||
|
if (!IS_ERR(table) && table[0] >= lookup_table_start) {
|
||||||
|
kfree(table);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -51,7 +51,7 @@ extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int);
|
|||||||
extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
|
extern void *squashfs_decompressor_init(struct super_block *, unsigned short);
|
||||||
|
|
||||||
/* export.c */
|
/* export.c */
|
||||||
extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
|
extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64,
|
||||||
unsigned int);
|
unsigned int);
|
||||||
|
|
||||||
/* fragment.c */
|
/* fragment.c */
|
||||||
|
@@ -245,6 +245,7 @@ allocate_id_index_table:
|
|||||||
msblk->id_table = NULL;
|
msblk->id_table = NULL;
|
||||||
goto failed_mount;
|
goto failed_mount;
|
||||||
}
|
}
|
||||||
|
next_table = msblk->id_table[0];
|
||||||
|
|
||||||
/* Handle inode lookup table */
|
/* Handle inode lookup table */
|
||||||
lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
|
lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
|
||||||
@@ -253,7 +254,7 @@ allocate_id_index_table:
|
|||||||
|
|
||||||
/* Allocate and read inode lookup table */
|
/* Allocate and read inode lookup table */
|
||||||
msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
|
msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
|
||||||
lookup_table_start, msblk->inodes);
|
lookup_table_start, next_table, msblk->inodes);
|
||||||
if (IS_ERR(msblk->inode_lookup_table)) {
|
if (IS_ERR(msblk->inode_lookup_table)) {
|
||||||
ERROR("unable to read inode lookup table\n");
|
ERROR("unable to read inode lookup table\n");
|
||||||
err = PTR_ERR(msblk->inode_lookup_table);
|
err = PTR_ERR(msblk->inode_lookup_table);
|
||||||
|
Reference in New Issue
Block a user