From 82de647e1f81fd89afc48608d889dd3b33cb8983 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Fri, 20 May 2011 02:26:43 +0100 Subject: [PATCH 1/8] Squashfs: move table allocation into squashfs_read_table() This eliminates a lot of duplicate code. Signed-off-by: Phillip Lougher --- fs/squashfs/cache.c | 29 +++++++++++++++++++++++------ fs/squashfs/export.c | 19 +------------------ fs/squashfs/fragment.c | 19 +------------------ fs/squashfs/id.c | 18 +----------------- fs/squashfs/squashfs.h | 2 +- fs/squashfs/super.c | 21 ++++++++------------- fs/squashfs/xattr_id.c | 34 +++++++++------------------------- 7 files changed, 44 insertions(+), 98 deletions(-) diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 26b15ae34d6f..20c8df1b7efc 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -393,19 +393,36 @@ struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb, /* * Read a filesystem table (uncompressed sequence of bytes) from disk */ -int squashfs_read_table(struct super_block *sb, void *buffer, u64 block, - int length) +void *squashfs_read_table(struct super_block *sb, u64 block, int length) { int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; int i, res; - void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL); - if (data == NULL) - return -ENOMEM; + void *table, *buffer, **data; + + table = buffer = kmalloc(length, GFP_KERNEL); + if (table == NULL) + return ERR_PTR(-ENOMEM); + + data = kcalloc(pages, sizeof(void *), GFP_KERNEL); + if (data == NULL) { + res = -ENOMEM; + goto failed; + } for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) data[i] = buffer; + res = squashfs_read_data(sb, data, block, length | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); + kfree(data); - return res; + + if (res < 0) + goto failed; + + return table; + +failed: + kfree(table); + return ERR_PTR(res); } diff --git a/fs/squashfs/export.c b/fs/squashfs/export.c index 7f93d5a9ee05..cc6dd90cee6a 100644 --- a/fs/squashfs/export.c +++ b/fs/squashfs/export.c @@ -124,27 +124,10 @@ __le64 *squashfs_read_inode_lookup_table(struct super_block *sb, u64 lookup_table_start, unsigned int inodes) { unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes); - __le64 *inode_lookup_table; - int err; TRACE("In read_inode_lookup_table, length %d\n", length); - /* Allocate inode lookup table indexes */ - inode_lookup_table = kmalloc(length, GFP_KERNEL); - if (inode_lookup_table == NULL) { - ERROR("Failed to allocate inode lookup table\n"); - return ERR_PTR(-ENOMEM); - } - - err = squashfs_read_table(sb, inode_lookup_table, lookup_table_start, - length); - if (err < 0) { - ERROR("unable to read inode lookup table\n"); - kfree(inode_lookup_table); - return ERR_PTR(err); - } - - return inode_lookup_table; + return squashfs_read_table(sb, lookup_table_start, length); } diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c index 7eef571443c6..567093db5870 100644 --- a/fs/squashfs/fragment.c +++ b/fs/squashfs/fragment.c @@ -74,23 +74,6 @@ __le64 *squashfs_read_fragment_index_table(struct super_block *sb, u64 fragment_table_start, unsigned int fragments) { unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments); - __le64 *fragment_index; - int err; - /* Allocate fragment lookup table indexes */ - fragment_index = kmalloc(length, GFP_KERNEL); - if (fragment_index == NULL) { - ERROR("Failed to allocate fragment index table\n"); - return ERR_PTR(-ENOMEM); - } - - err = squashfs_read_table(sb, fragment_index, fragment_table_start, - length); - if (err < 0) { - ERROR("unable to read fragment index table\n"); - kfree(fragment_index); - return ERR_PTR(err); - } - - return fragment_index; + return squashfs_read_table(sb, fragment_table_start, length); } diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c index d8f32452638e..2fdac6703b48 100644 --- a/fs/squashfs/id.c +++ b/fs/squashfs/id.c @@ -69,24 +69,8 @@ __le64 *squashfs_read_id_index_table(struct super_block *sb, u64 id_table_start, unsigned short no_ids) { unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids); - __le64 *id_table; - int err; TRACE("In read_id_index_table, length %d\n", length); - /* Allocate id lookup table indexes */ - id_table = kmalloc(length, GFP_KERNEL); - if (id_table == NULL) { - ERROR("Failed to allocate id index table\n"); - return ERR_PTR(-ENOMEM); - } - - err = squashfs_read_table(sb, id_table, id_table_start, length); - if (err < 0) { - ERROR("unable to read id index table\n"); - kfree(id_table); - return ERR_PTR(err); - } - - return id_table; + return squashfs_read_table(sb, id_table_start, length); } diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 1f2e608b8785..d622e849a451 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -44,7 +44,7 @@ extern struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *, u64, int); extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *, u64, int); -extern int squashfs_read_table(struct super_block *, void *, u64, int); +extern void *squashfs_read_table(struct super_block *, u64, int); /* decompressor.c */ extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 5c8184c061a4..d16c39263f39 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -95,12 +95,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) } msblk = sb->s_fs_info; - sblk = kzalloc(sizeof(*sblk), GFP_KERNEL); - if (sblk == NULL) { - ERROR("Failed to allocate squashfs_super_block\n"); - goto failure; - } - msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE); msblk->devblksize_log2 = ffz(~msblk->devblksize); @@ -114,10 +108,12 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) * of bytes_used) we need to set it to an initial sensible dummy value */ msblk->bytes_used = sizeof(*sblk); - err = squashfs_read_table(sb, sblk, SQUASHFS_START, sizeof(*sblk)); + sblk = squashfs_read_table(sb, SQUASHFS_START, sizeof(*sblk)); - if (err < 0) { + if (IS_ERR(sblk)) { ERROR("unable to read squashfs_super_block\n"); + err = PTR_ERR(sblk); + sblk = NULL; goto failed_mount; } @@ -222,6 +218,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->id_table = squashfs_read_id_index_table(sb, le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids)); if (IS_ERR(msblk->id_table)) { + ERROR("unable to read id index table\n"); err = PTR_ERR(msblk->id_table); msblk->id_table = NULL; goto failed_mount; @@ -242,6 +239,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->fragment_index = squashfs_read_fragment_index_table(sb, le64_to_cpu(sblk->fragment_table_start), fragments); if (IS_ERR(msblk->fragment_index)) { + ERROR("unable to read fragment index table\n"); err = PTR_ERR(msblk->fragment_index); msblk->fragment_index = NULL; goto failed_mount; @@ -256,6 +254,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb, lookup_table_start, msblk->inodes); if (IS_ERR(msblk->inode_lookup_table)) { + ERROR("unable to read inode lookup table\n"); err = PTR_ERR(msblk->inode_lookup_table); msblk->inode_lookup_table = NULL; goto failed_mount; @@ -273,6 +272,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->xattr_id_table = squashfs_read_xattr_id_table(sb, xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids); if (IS_ERR(msblk->xattr_id_table)) { + ERROR("unable to read xattr id index table\n"); err = PTR_ERR(msblk->xattr_id_table); msblk->xattr_id_table = NULL; if (err != -ENOTSUPP) @@ -318,11 +318,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_fs_info = NULL; kfree(sblk); return err; - -failure: - kfree(sb->s_fs_info); - sb->s_fs_info = NULL; - return -ENOMEM; } diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c index 05385dbe1465..51a7bd0cc449 100644 --- a/fs/squashfs/xattr_id.c +++ b/fs/squashfs/xattr_id.c @@ -67,34 +67,18 @@ __le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 start, u64 *xattr_table_start, int *xattr_ids) { unsigned int len; - __le64 *xid_table; - struct squashfs_xattr_id_table id_table; - int err; + struct squashfs_xattr_id_table *id_table; - err = squashfs_read_table(sb, &id_table, start, sizeof(id_table)); - if (err < 0) { - ERROR("unable to read xattr id table\n"); - return ERR_PTR(err); - } - *xattr_table_start = le64_to_cpu(id_table.xattr_table_start); - *xattr_ids = le32_to_cpu(id_table.xattr_ids); + id_table = squashfs_read_table(sb, start, sizeof(*id_table)); + if (IS_ERR(id_table)) + return (__le64 *) id_table; + + *xattr_table_start = le64_to_cpu(id_table->xattr_table_start); + *xattr_ids = le32_to_cpu(id_table->xattr_ids); + kfree(id_table); len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids); TRACE("In read_xattr_index_table, length %d\n", len); - /* Allocate xattr id lookup table indexes */ - xid_table = kmalloc(len, GFP_KERNEL); - if (xid_table == NULL) { - ERROR("Failed to allocate xattr id index table\n"); - return ERR_PTR(-ENOMEM); - } - - err = squashfs_read_table(sb, xid_table, start + sizeof(id_table), len); - if (err < 0) { - ERROR("unable to read xattr id index table\n"); - kfree(xid_table); - return ERR_PTR(err); - } - - return xid_table; + return squashfs_read_table(sb, start + sizeof(*id_table), len); } From 76e002f755b61aa79228f4e751bbca8674aba0f6 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 24 May 2011 02:57:05 +0100 Subject: [PATCH 2/8] Squashfs: reverse order of filesystem table reading Reverse order of table reading from mostly first to last in placement order, to last to first. This is to enable extra superblock sanity checks to be added in later patches. Signed-off-by: Phillip Lougher --- fs/squashfs/super.c | 71 +++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index d16c39263f39..401cc8c7608f 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -214,6 +214,24 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } + /* Handle xattrs */ + sb->s_xattr = squashfs_xattr_handlers; + xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start); + if (xattr_id_table_start == SQUASHFS_INVALID_BLK) + goto allocate_id_index_table; + + /* Allocate and read xattr id lookup table */ + msblk->xattr_id_table = squashfs_read_xattr_id_table(sb, + xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids); + if (IS_ERR(msblk->xattr_id_table)) { + ERROR("unable to read xattr id index table\n"); + err = PTR_ERR(msblk->xattr_id_table); + msblk->xattr_id_table = NULL; + if (err != -ENOTSUPP) + goto failed_mount; + } + +allocate_id_index_table: /* Allocate and read id index table */ msblk->id_table = squashfs_read_id_index_table(sb, le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids)); @@ -224,9 +242,27 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } + /* Handle inode lookup table */ + lookup_table_start = le64_to_cpu(sblk->lookup_table_start); + if (lookup_table_start == SQUASHFS_INVALID_BLK) + goto handle_fragments; + + /* Allocate and read inode lookup table */ + msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb, + lookup_table_start, msblk->inodes); + if (IS_ERR(msblk->inode_lookup_table)) { + ERROR("unable to read inode lookup table\n"); + err = PTR_ERR(msblk->inode_lookup_table); + msblk->inode_lookup_table = NULL; + goto failed_mount; + } + + sb->s_export_op = &squashfs_export_ops; + +handle_fragments: fragments = le32_to_cpu(sblk->fragments); if (fragments == 0) - goto allocate_lookup_table; + goto allocate_root; msblk->fragment_cache = squashfs_cache_init("fragment", SQUASHFS_CACHED_FRAGMENTS, msblk->block_size); @@ -245,39 +281,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } -allocate_lookup_table: - lookup_table_start = le64_to_cpu(sblk->lookup_table_start); - if (lookup_table_start == SQUASHFS_INVALID_BLK) - goto allocate_xattr_table; - - /* Allocate and read inode lookup table */ - msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb, - lookup_table_start, msblk->inodes); - if (IS_ERR(msblk->inode_lookup_table)) { - ERROR("unable to read inode lookup table\n"); - err = PTR_ERR(msblk->inode_lookup_table); - msblk->inode_lookup_table = NULL; - goto failed_mount; - } - - sb->s_export_op = &squashfs_export_ops; - -allocate_xattr_table: - sb->s_xattr = squashfs_xattr_handlers; - xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start); - if (xattr_id_table_start == SQUASHFS_INVALID_BLK) - goto allocate_root; - - /* Allocate and read xattr id lookup table */ - msblk->xattr_id_table = squashfs_read_xattr_id_table(sb, - xattr_id_table_start, &msblk->xattr_table, &msblk->xattr_ids); - if (IS_ERR(msblk->xattr_id_table)) { - ERROR("unable to read xattr id index table\n"); - err = PTR_ERR(msblk->xattr_id_table); - msblk->xattr_id_table = NULL; - if (err != -ENOTSUPP) - goto failed_mount; - } allocate_root: root = new_inode(sb); if (!root) { From 6f04864515365e135adc9f1cee4ac1251bb0ed35 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 24 May 2011 03:20:27 +0100 Subject: [PATCH 3/8] Squashfs: add sanity checks to xattr reading at mount time These checks add sanity checking of the mount-time xattr structures. Signed-off-by: Phillip Lougher --- fs/squashfs/xattr_id.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c index 51a7bd0cc449..f60fb3cce183 100644 --- a/fs/squashfs/xattr_id.c +++ b/fs/squashfs/xattr_id.c @@ -76,6 +76,17 @@ __le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 start, *xattr_table_start = le64_to_cpu(id_table->xattr_table_start); *xattr_ids = le32_to_cpu(id_table->xattr_ids); kfree(id_table); + + /* Sanity check values */ + + /* there is always at least one xattr id */ + if (*xattr_ids == 0) + return ERR_PTR(-EINVAL); + + /* xattr_table should be less than start */ + if (*xattr_table_start >= start) + return ERR_PTR(-EINVAL); + len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids); TRACE("In read_xattr_index_table, length %d\n", len); From 37986f63c81bf23c856f65fc5e4830550e7f3d5b Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 24 May 2011 04:05:22 +0100 Subject: [PATCH 4/8] Squashfs: add sanity checks to id reading at mount time Fsfuzzer generates corrupted filesystems which throw a warn_on in kmalloc. One of these is due to a corrupted superblock no_ids 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 id table structures. Signed-off-by: Phillip Lougher --- fs/squashfs/id.c | 30 ++++++++++++++++++++++++++++-- fs/squashfs/squashfs.h | 2 +- fs/squashfs/super.c | 10 +++++++--- fs/squashfs/xattr.h | 1 + 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c index 2fdac6703b48..499d08a1f34a 100644 --- a/fs/squashfs/id.c +++ b/fs/squashfs/id.c @@ -66,11 +66,37 @@ int squashfs_get_id(struct super_block *sb, unsigned int index, * Read uncompressed id lookup table indexes from disk into memory */ __le64 *squashfs_read_id_index_table(struct super_block *sb, - u64 id_table_start, unsigned short no_ids) + u64 id_table_start, u64 next_table, unsigned short no_ids) { unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids); + __le64 *table; TRACE("In read_id_index_table, length %d\n", length); - return squashfs_read_table(sb, id_table_start, length); + /* Sanity check values */ + + /* there should always be at least one id */ + if (no_ids == 0) + return ERR_PTR(-EINVAL); + + /* + * length bytes should not extend into the next table - this check + * also traps instances where id_table_start is incorrectly larger + * than the next table start + */ + if (id_table_start + length > next_table) + return ERR_PTR(-EINVAL); + + table = squashfs_read_table(sb, id_table_start, length); + + /* + * table[0] points to the first id lookup table metadata block, this + * should be less than id_table_start + */ + if (!IS_ERR(table) && table[0] >= id_table_start) { + kfree(table); + return ERR_PTR(-EINVAL); + } + + return table; } diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index d622e849a451..0e3d220defd2 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -61,7 +61,7 @@ extern __le64 *squashfs_read_fragment_index_table(struct super_block *, /* id.c */ extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); -extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, +extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64, unsigned short); /* inode.c */ diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 401cc8c7608f..8f5f2781a60e 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -83,7 +83,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) long long root_inode; unsigned short flags; unsigned int fragments; - u64 lookup_table_start, xattr_id_table_start; + u64 lookup_table_start, xattr_id_table_start, next_table; int err; TRACE("Entered squashfs_fill_superblock\n"); @@ -217,8 +217,10 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) /* Handle xattrs */ sb->s_xattr = squashfs_xattr_handlers; xattr_id_table_start = le64_to_cpu(sblk->xattr_id_table_start); - if (xattr_id_table_start == SQUASHFS_INVALID_BLK) + if (xattr_id_table_start == SQUASHFS_INVALID_BLK) { + next_table = msblk->bytes_used; goto allocate_id_index_table; + } /* Allocate and read xattr id lookup table */ msblk->xattr_id_table = squashfs_read_xattr_id_table(sb, @@ -230,11 +232,13 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) if (err != -ENOTSUPP) goto failed_mount; } + next_table = msblk->xattr_table; allocate_id_index_table: /* Allocate and read id index table */ msblk->id_table = squashfs_read_id_index_table(sb, - le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids)); + le64_to_cpu(sblk->id_table_start), next_table, + le16_to_cpu(sblk->no_ids)); if (IS_ERR(msblk->id_table)) { ERROR("unable to read id index table\n"); err = PTR_ERR(msblk->id_table); diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h index b634efce4bde..9b3dd6c4c1fe 100644 --- a/fs/squashfs/xattr.h +++ b/fs/squashfs/xattr.h @@ -31,6 +31,7 @@ static inline __le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 start, u64 *xattr_table_start, int *xattr_ids) { ERROR("Xattrs in filesystem, these will be ignored\n"); + *xattr_table_start = start; return ERR_PTR(-ENOTSUPP); } From ac51a0a7139aa93bf1176b701c86fa3d2bdf6106 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 24 May 2011 04:15:21 +0100 Subject: [PATCH 5/8] 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 --- fs/squashfs/export.c | 29 +++++++++++++++++++++++++++-- fs/squashfs/squashfs.h | 2 +- fs/squashfs/super.c | 3 ++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/fs/squashfs/export.c b/fs/squashfs/export.c index cc6dd90cee6a..e3108711a40a 100644 --- a/fs/squashfs/export.c +++ b/fs/squashfs/export.c @@ -121,13 +121,38 @@ static struct dentry *squashfs_get_parent(struct dentry *child) * Read uncompressed inode lookup table indexes off disk into memory */ __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); + __le64 *table; 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; } diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 0e3d220defd2..3b705f137ebb 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -51,7 +51,7 @@ extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); extern void *squashfs_decompressor_init(struct super_block *, unsigned short); /* 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); /* fragment.c */ diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 8f5f2781a60e..80a7119870a6 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -245,6 +245,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->id_table = NULL; goto failed_mount; } + next_table = msblk->id_table[0]; /* Handle inode lookup table */ lookup_table_start = le64_to_cpu(sblk->lookup_table_start); @@ -253,7 +254,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) /* Allocate and read inode lookup table */ 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)) { ERROR("unable to read inode lookup table\n"); err = PTR_ERR(msblk->inode_lookup_table); From 1cac63cc9b2ff0d16ab2d16232b1a6ee8676a47b Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 24 May 2011 04:33:34 +0100 Subject: [PATCH 6/8] Squashfs: add sanity checks to fragment reading at mount time Fsfuzzer generates corrupted filesystems which throw a warn_on in kmalloc. One of these is due to a corrupted superblock fragments 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 fragment table structures. Signed-off-by: Phillip Lougher --- fs/squashfs/fragment.c | 24 ++++++++++++++++++++++-- fs/squashfs/squashfs.h | 2 +- fs/squashfs/super.c | 3 ++- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c index 567093db5870..bfd707569ab7 100644 --- a/fs/squashfs/fragment.c +++ b/fs/squashfs/fragment.c @@ -71,9 +71,29 @@ int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment, * Read the uncompressed fragment lookup table indexes off disk into memory */ __le64 *squashfs_read_fragment_index_table(struct super_block *sb, - u64 fragment_table_start, unsigned int fragments) + u64 fragment_table_start, u64 next_table, unsigned int fragments) { unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments); + __le64 *table; - return squashfs_read_table(sb, fragment_table_start, length); + /* + * Sanity check, length bytes should not extend into the next table - + * this check also traps instances where fragment_table_start is + * incorrectly larger than the next table start + */ + if (fragment_table_start + length > next_table) + return ERR_PTR(-EINVAL); + + table = squashfs_read_table(sb, fragment_table_start, length); + + /* + * table[0] points to the first fragment table metadata block, this + * should be less than fragment_table_start + */ + if (!IS_ERR(table) && table[0] >= fragment_table_start) { + kfree(table); + return ERR_PTR(-EINVAL); + } + + return table; } diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 3b705f137ebb..647b81b477d4 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -57,7 +57,7 @@ extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64, /* fragment.c */ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *); extern __le64 *squashfs_read_fragment_index_table(struct super_block *, - u64, unsigned int); + u64, u64, unsigned int); /* id.c */ extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 80a7119870a6..efa8118260d4 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -261,6 +261,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->inode_lookup_table = NULL; goto failed_mount; } + next_table = msblk->inode_lookup_table[0]; sb->s_export_op = &squashfs_export_ops; @@ -278,7 +279,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) /* Allocate and read fragment index table */ msblk->fragment_index = squashfs_read_fragment_index_table(sb, - le64_to_cpu(sblk->fragment_table_start), fragments); + le64_to_cpu(sblk->fragment_table_start), next_table, fragments); if (IS_ERR(msblk->fragment_index)) { ERROR("unable to read fragment index table\n"); err = PTR_ERR(msblk->fragment_index); From 1094a4a6118019255bf0e4adaa96bb17ccec3a82 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 24 May 2011 04:45:33 +0100 Subject: [PATCH 7/8] Squashfs: add extra sanity checks at mount time Add some extra sanity checks of the inode and directory structures. Signed-off-by: Phillip Lougher --- fs/squashfs/super.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index efa8118260d4..984d6c861928 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -268,7 +268,7 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) handle_fragments: fragments = le32_to_cpu(sblk->fragments); if (fragments == 0) - goto allocate_root; + goto check_directory_table; msblk->fragment_cache = squashfs_cache_init("fragment", SQUASHFS_CACHED_FRAGMENTS, msblk->block_size); @@ -286,8 +286,22 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) msblk->fragment_index = NULL; goto failed_mount; } + next_table = msblk->fragment_index[0]; -allocate_root: +check_directory_table: + /* Sanity check directory_table */ + if (msblk->directory_table >= next_table) { + err = -EINVAL; + goto failed_mount; + } + + /* Sanity check inode_table */ + if (msblk->inode_table >= msblk->directory_table) { + err = -EINVAL; + goto failed_mount; + } + + /* allocate root */ root = new_inode(sb); if (!root) { err = -ENOMEM; From d7f2ff6718efa155fd92e481a5960496d084c63f Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Thu, 26 May 2011 10:39:56 +0100 Subject: [PATCH 8/8] Squashfs: update email address My existing email address may stop working in a month or two, so update email to one that will continue working. Signed-off-by: Phillip Lougher --- MAINTAINERS | 2 +- fs/squashfs/block.c | 2 +- fs/squashfs/cache.c | 2 +- fs/squashfs/decompressor.c | 2 +- fs/squashfs/decompressor.h | 2 +- fs/squashfs/dir.c | 2 +- fs/squashfs/export.c | 2 +- fs/squashfs/file.c | 2 +- fs/squashfs/fragment.c | 2 +- fs/squashfs/id.c | 2 +- fs/squashfs/inode.c | 2 +- fs/squashfs/namei.c | 2 +- fs/squashfs/squashfs.h | 2 +- fs/squashfs/squashfs_fs.h | 2 +- fs/squashfs/squashfs_fs_i.h | 2 +- fs/squashfs/squashfs_fs_sb.h | 2 +- fs/squashfs/super.c | 4 ++-- fs/squashfs/symlink.c | 2 +- fs/squashfs/xattr.c | 2 +- fs/squashfs/xattr.h | 2 +- fs/squashfs/xattr_id.c | 2 +- fs/squashfs/xz_wrapper.c | 2 +- fs/squashfs/zlib_wrapper.c | 2 +- 23 files changed, 24 insertions(+), 24 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 531c5cf150ba..b28f6a7ca9d7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5906,7 +5906,7 @@ F: Documentation/filesystems/spufs.txt F: arch/powerpc/platforms/cell/spufs/ SQUASHFS FILE SYSTEM -M: Phillip Lougher +M: Phillip Lougher L: squashfs-devel@lists.sourceforge.net (subscribers-only) W: http://squashfs.org.uk S: Maintained diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c index 8ab48bc2fa7d..ed0eb2a921f4 100644 --- a/fs/squashfs/block.c +++ b/fs/squashfs/block.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index 20c8df1b7efc..7567f7374195 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c index e921bd213738..9f1b0bb96f13 100644 --- a/fs/squashfs/decompressor.c +++ b/fs/squashfs/decompressor.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h index 099745ad5691..8ba70cff09a6 100644 --- a/fs/squashfs/decompressor.h +++ b/fs/squashfs/decompressor.h @@ -4,7 +4,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index 3f79cd1d0c19..9dfe2ce0fb70 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/export.c b/fs/squashfs/export.c index e3108711a40a..730c56248c9b 100644 --- a/fs/squashfs/export.c +++ b/fs/squashfs/export.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index a25c5060bdcb..38bb1c640559 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/fragment.c b/fs/squashfs/fragment.c index bfd707569ab7..1516a6490bfb 100644 --- a/fs/squashfs/fragment.c +++ b/fs/squashfs/fragment.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/id.c b/fs/squashfs/id.c index 499d08a1f34a..a70858e0fb44 100644 --- a/fs/squashfs/id.c +++ b/fs/squashfs/id.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/inode.c b/fs/squashfs/inode.c index 62e63ad25075..04bebcaa2373 100644 --- a/fs/squashfs/inode.c +++ b/fs/squashfs/inode.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c index 5d922a6701ab..4bc63ac64bc0 100644 --- a/fs/squashfs/namei.c +++ b/fs/squashfs/namei.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 647b81b477d4..e3be6a71cfa7 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h index 4582c568ef4d..b4a4e539a08c 100644 --- a/fs/squashfs/squashfs_fs.h +++ b/fs/squashfs/squashfs_fs.h @@ -4,7 +4,7 @@ * Squashfs * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/squashfs_fs_i.h b/fs/squashfs/squashfs_fs_i.h index 359baefc01fc..73588e7700ed 100644 --- a/fs/squashfs/squashfs_fs_i.h +++ b/fs/squashfs/squashfs_fs_i.h @@ -4,7 +4,7 @@ * Squashfs * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h index d9037a5215f0..651f0b31d296 100644 --- a/fs/squashfs/squashfs_fs_sb.h +++ b/fs/squashfs/squashfs_fs_sb.h @@ -4,7 +4,7 @@ * Squashfs * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 984d6c861928..6f26abee3597 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -493,5 +493,5 @@ static const struct super_operations squashfs_super_ops = { module_init(init_squashfs_fs); module_exit(exit_squashfs_fs); MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem"); -MODULE_AUTHOR("Phillip Lougher "); +MODULE_AUTHOR("Phillip Lougher "); MODULE_LICENSE("GPL"); diff --git a/fs/squashfs/symlink.c b/fs/squashfs/symlink.c index ec86434921e1..1191817264cc 100644 --- a/fs/squashfs/symlink.c +++ b/fs/squashfs/symlink.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/xattr.c b/fs/squashfs/xattr.c index 3876c36699a1..92fcde7b4d61 100644 --- a/fs/squashfs/xattr.c +++ b/fs/squashfs/xattr.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2010 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h index 9b3dd6c4c1fe..c83f5d9ec125 100644 --- a/fs/squashfs/xattr.h +++ b/fs/squashfs/xattr.h @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2010 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c index f60fb3cce183..c89607d690c4 100644 --- a/fs/squashfs/xattr_id.c +++ b/fs/squashfs/xattr_id.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2010 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c index aa47a286d1f8..1760b7d108f6 100644 --- a/fs/squashfs/xz_wrapper.c +++ b/fs/squashfs/xz_wrapper.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 517688b32ffa..55d918fd2d86 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c @@ -2,7 +2,7 @@ * Squashfs - a compressed read only filesystem for Linux * * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 - * Phillip Lougher + * Phillip Lougher * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License