squashfs-tools/6009-CVE-2015-464.patch
2019-09-30 11:17:34 -04:00

427 lines
14 KiB
Diff

From 6831ab9f5ae7c8d224a2b8075bd04815ed7d5e17 Mon Sep 17 00:00:00 2001
From: renxudong <renxudong1@huawei.com>
Date: Sun, 11 Aug 2019 03:17:20 -0400
Subject: [PATCH] CVE-2015-464
---
squashfs-tools/read_xattrs.c | 57 ++++++++---
squashfs-tools/unsquash-4.c | 232 +++++++++++++++++++++++++++++++++++--------
2 files changed, 236 insertions(+), 53 deletions(-)
diff --git a/squashfs-tools/read_xattrs.c b/squashfs-tools/read_xattrs.c
index 9c66387..189a8a1 100644
--- a/squashfs-tools/read_xattrs.c
+++ b/squashfs-tools/read_xattrs.c
@@ -150,7 +150,16 @@ static int read_xattr_entry(struct xattr_list *xattr,
*/
int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, long long *table_start)
{
- int res, bytes, i, indexes, index_bytes, ids;
+ /*
+ * Note on overflow limits:
+ * Size of ids (id_table.xattr_ids) is 2^32 (unsigned int)
+ * Max size of bytes is 2^32*16 or 2^36
+ * Max indexes is (2^32*16)/8K or 2^23
+ * Max index_bytes is ((2^32*16)/8K)*8 or 2^26 or 64M
+ */
+ int res, i, indexes, index_bytes;
+ unsigned int ids;
+ long long bytes;
long long *index, start, end;
struct squashfs_xattr_table id_table;
@@ -170,24 +179,44 @@ int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, l
SQUASHFS_INSWAP_XATTR_TABLE(&id_table);
- if(flag) {
- /*
- * id_table.xattr_table_start stores the start of the compressed xattr
- * * metadata blocks. This by definition is also the end of the previous
- * filesystem table - the id lookup table.
- */
+ /*
+ * Compute index table values
+ */
+ ids = id_table.xattr_ids;
+ xattr_table_start = id_table.xattr_table_start;
+ index_bytes = SQUASHFS_XATTR_BLOCK_BYTES((long long) ids);
+ indexes = SQUASHFS_XATTR_BLOCKS((long long) ids);
+
+ /*
+ * The size of the index table (index_bytes) should match the
+ * table start and end points
+ */
+ if(index_bytes != (sBlk->bytes_used - (sBlk->xattr_id_table_start + sizeof(id_table)))) {
+ ERROR("read_xattrs_from_disk: Bad xattr_ids count in super block\n");
+ return 0;
+ }
+
+ /*
+ * id_table.xattr_table_start stores the start of the compressed xattr
+ * metadata blocks. This by definition is also the end of the previous
+ * filesystem table - the id lookup table.
+ */
+ if(table_start != NULL)
*table_start = id_table.xattr_table_start;
+
+ /*
+ * If flag is set then return once we've read the above
+ * table_start. That value is necessary for sanity checking,
+ * but we don't actually want to extract the xattrs, and so
+ * stop here.
+ */
+ if(flag)
return id_table.xattr_ids;
- }
/*
* Allocate and read the index to the xattr id table metadata
* blocks
*/
- ids = id_table.xattr_ids;
- xattr_table_start = id_table.xattr_table_start;
- index_bytes = SQUASHFS_XATTR_BLOCK_BYTES(ids);
- indexes = SQUASHFS_XATTR_BLOCKS(ids);
index = malloc(index_bytes);
if(index == NULL)
MEM_ERROR();
@@ -203,7 +232,7 @@ int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, l
* Allocate enough space for the uncompressed xattr id table, and
* read and decompress it
*/
- bytes = SQUASHFS_XATTR_BYTES(ids);
+ bytes = SQUASHFS_XATTR_BYTES((long long) ids);
xattr_ids = malloc(bytes);
if(xattr_ids == NULL)
MEM_ERROR();
@@ -213,7 +242,7 @@ int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, l
bytes & (SQUASHFS_METADATA_SIZE - 1);
int length = read_block(fd, index[i], NULL, expected,
((unsigned char *) xattr_ids) +
- (i * SQUASHFS_METADATA_SIZE));
+ ((long long) i * SQUASHFS_METADATA_SIZE));
TRACE("Read xattr id table block %d, from 0x%llx, length "
"%d\n", i, index[i], length);
if(length == 0) {
diff --git a/squashfs-tools/unsquash-4.c b/squashfs-tools/unsquash-4.c
index 02b5cfc..a1da4c7 100644
--- a/squashfs-tools/unsquash-4.c
+++ b/squashfs-tools/unsquash-4.c
@@ -29,23 +29,55 @@
static struct squashfs_fragment_entry *fragment_table;
static unsigned int *id_table;
-static int read_fragment_table(long long *directory_table_end)
+long long *alloc_index_table(int indexes)
{
+ static long long *alloc_table = NULL;
+ static int alloc_size = 0;
+ int length = indexes * sizeof(long long);
+
+ if(alloc_size < length) {
+ long long *table = realloc(alloc_table, length);
+
+ if(table == NULL)
+ EXIT_UNSQUASH("alloc_index_table: failed to allocate "
+ "index table\n");
+
+ alloc_table = table;
+ alloc_size = length;
+ }
+
+ return alloc_table;
+}
+
+static int read_fragment_table(long long *table_start)
+{
+ /*
+ * Note on overflow limits:
+ * Size of SBlk.s.fragments is 2^32 (unsigned int)
+ * Max size of bytes is 2^32*16 or 2^36
+ * Max indexes is (2^32*16)/8K or 2^23
+ * Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
+ */
int res, i;
- size_t bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments);
- size_t indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.s.fragments);
+ long long bytes = SQUASHFS_FRAGMENT_BYTES((long long) sBlk.s.fragments);
+ int indexes = SQUASHFS_FRAGMENT_INDEXES((long long) sBlk.s.fragments);
+ int length = SQUASHFS_FRAGMENT_INDEX_BYTES((long long) sBlk.s.fragments);
long long *fragment_table_index;
+ /*
+ * The size of the index table (length bytes) should match the
+ * table start and end points
+ */
+ if(length != (*table_start - sBlk.s.fragment_table_start)) {
+ ERROR("read_fragment_table: Bad fragment count in super block\n");
+ return FALSE;
+ }
+
TRACE("read_fragment_table: %u fragments, reading %zu fragment indexes "
"from 0x%llx\n", sBlk.s.fragments, indexes,
sBlk.s.fragment_table_start);
- if(sBlk.s.fragments == 0) {
- *directory_table_end = sBlk.s.fragment_table_start;
- return TRUE;
- }
-
- fragment_table_index = malloc(indexes*sizeof(long long));
+ fragment_table_index = alloc_index_table(indexes);
if(fragment_table_index == NULL)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table index\n");
@@ -55,9 +87,8 @@ static int read_fragment_table(long long *directory_table_end)
EXIT_UNSQUASH("read_fragment_table: failed to allocate "
"fragment table\n");
- res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
- SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk.s.fragments),
- fragment_table_index);
+ res = read_fs_bytes(fd, sBlk.s.fragment_table_start, length,
+ fragment_table_index);
if(res == FALSE) {
ERROR("read_fragment_table: failed to read fragment table "
"index\n");
@@ -83,7 +114,7 @@ static int read_fragment_table(long long *directory_table_end)
for(i = 0; i < sBlk.s.fragments; i++)
SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
- *directory_table_end = fragment_table_index[0];
+ *table_start = fragment_table_index[0];
return TRUE;
}
@@ -361,25 +392,42 @@ corrupted:
}
-static int read_uids_guids(long long *table_start)
+static int read_id_table(long long *table_start)
{
+ /*
+ * Note on overflow limits:
+ * Size of SBlk.s.no_ids is 2^16 (unsigned short)
+ * Max size of bytes is 2^16*4 or 256K
+ * Max indexes is (2^16*4)/8K or 32
+ * Max length is ((2^16*4)/8K)*8 or 256
+ */
int res, i;
int bytes = SQUASHFS_ID_BYTES(sBlk.s.no_ids);
int indexes = SQUASHFS_ID_BLOCKS(sBlk.s.no_ids);
- long long id_index_table[indexes];
+ int length = SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids);
+ long long *id_index_table;
- TRACE("read_uids_guids: no_ids %d\n", sBlk.s.no_ids);
+ /*
+ * The size of the index table (length bytes) should match the
+ * table start and end points
+ */
+ if(length != (*table_start - sBlk.s.id_table_start)) {
+ ERROR("read_id_table: Bad id count in super block\n");
+ return FALSE;
+ }
+
+ TRACE("read_id_table: no_ids %d\n", sBlk.s.no_ids);
+ id_index_table = alloc_index_table(indexes);
id_table = malloc(bytes);
if(id_table == NULL) {
- ERROR("read_uids_guids: failed to allocate id table\n");
+ ERROR("read_id_table: failed to allocate id table\n");
return FALSE;
}
- res = read_fs_bytes(fd, sBlk.s.id_table_start,
- SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids), id_index_table);
+ res = read_fs_bytes(fd, sBlk.s.id_table_start, length, id_index_table);
if(res == FALSE) {
- ERROR("read_uids_guids: failed to read id index table\n");
+ ERROR("read_id_table: failed to read id index table\n");
return FALSE;
}
SQUASHFS_INSWAP_ID_BLOCKS(id_index_table, indexes);
@@ -398,7 +446,7 @@ static int read_uids_guids(long long *table_start)
res = read_block(fd, id_index_table[i], NULL, expected,
((char *) id_table) + i * SQUASHFS_METADATA_SIZE);
if(res == FALSE) {
- ERROR("read_uids_guids: failed to read id table block"
+ ERROR("read_id_table: failed to read id table block"
"\n");
return FALSE;
}
@@ -412,12 +460,30 @@ static int read_uids_guids(long long *table_start)
static int parse_exports_table(long long *table_start)
{
+ /*
+ * Note on overflow limits:
+ * Size of SBlk.s.inodes is 2^32 (unsigned int)
+ * Max indexes is (2^32*8)/8K or 2^22
+ * Max length is ((2^32*8)/8K)*8 or 2^25
+ */
int res;
- int indexes = SQUASHFS_LOOKUP_BLOCKS(sBlk.s.inodes);
- long long export_index_table[indexes];
+ int indexes = SQUASHFS_LOOKUP_BLOCKS((long long) sBlk.s.inodes);
+ int length = SQUASHFS_LOOKUP_BLOCK_BYTES((long long) sBlk.s.inodes);
+ long long *export_index_table;
+
+ /*
+ * The size of the index table (length bytes) should match the
+ * table start and end points
+ */
+ if(length != (*table_start - sBlk.s.lookup_table_start)) {
+ ERROR("parse_exports_table: Bad inode count in super block\n");
+ return FALSE;
+ }
- res = read_fs_bytes(fd, sBlk.s.lookup_table_start,
- SQUASHFS_LOOKUP_BLOCK_BYTES(sBlk.s.inodes), export_index_table);
+ export_index_table = alloc_index_table(indexes);
+
+ res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
+ export_index_table);
if(res == FALSE) {
ERROR("parse_exports_table: failed to read export index table\n");
return FALSE;
@@ -437,30 +503,118 @@ static int parse_exports_table(long long *table_start)
int read_filesystem_tables_4()
{
- long long directory_table_end, table_start;
+ long long table_start;
- if(read_xattrs_from_disk(fd, &sBlk.s, no_xattrs, &table_start) == 0)
- return FALSE;
+ /* Read xattrs */
+ if(sBlk.s.xattr_id_table_start != SQUASHFS_INVALID_BLK) {
+ /* sanity check super block contents */
+ if(sBlk.s.xattr_id_table_start >= sBlk.s.bytes_used) {
+ ERROR("read_filesystem_tables: xattr id table start too large in super block\n");
+ goto corrupted;
+ }
- if(read_uids_guids(&table_start) == FALSE)
- return FALSE;
+ if(read_xattrs_from_disk(fd, &sBlk.s, no_xattrs, &table_start) == 0)
+ goto corrupted;
+ } else
+ table_start = sBlk.s.bytes_used;
- if(parse_exports_table(&table_start) == FALSE)
- return FALSE;
+ /* Read id lookup table */
- if(read_fragment_table(&directory_table_end) == FALSE)
- return FALSE;
+ /* Sanity check super block contents */
+ if(sBlk.s.id_table_start >= table_start) {
+ ERROR("read_filesystem_tables: id table start too large in super block\n");
+ goto corrupted;
+ }
- if(read_inode_table(sBlk.s.inode_table_start,
- sBlk.s.directory_table_start) == FALSE)
- return FALSE;
+ /* there should always be at least one id */
+ if(sBlk.s.no_ids == 0) {
+ ERROR("read_filesystem_tables: Bad id count in super block\n");
+ goto corrupted;
+ }
+
+ /*
+ * the number of ids can never be more than double the number of inodes
+ * (the maximum is a unique uid and gid for each inode).
+ */
+ if(sBlk.s.no_ids > (sBlk.s.inodes * 2L)) {
+ ERROR("read_filesystem_tables: Bad id count in super block\n");
+ goto corrupted;
+ }
+
+ if(read_id_table(&table_start) == FALSE)
+ goto corrupted;
+
+ /* Read exports table */
+ if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
+
+ /* sanity check super block contents */
+ if(sBlk.s.lookup_table_start >= table_start) {
+ ERROR("read_filesystem_tables: lookup table start too large in super block\n");
+ goto corrupted;
+ }
+
+ if(parse_exports_table(&table_start) == FALSE)
+ goto corrupted;
+ }
+
+ /* Read fragment table */
+ if(sBlk.s.fragments != 0) {
+
+ /* Sanity check super block contents */
+ if(sBlk.s.fragment_table_start >= table_start) {
+ ERROR("read_filesystem_tables: fragment table start too large in super block\n");
+ goto corrupted;
+ }
+
+ /* The number of fragments should not exceed the number of inodes */
+ if(sBlk.s.fragments > sBlk.s.inodes) {
+ ERROR("read_filesystem_tables: Bad fragment count in super block\n");
+ goto corrupted;
+ }
+
+ if(read_fragment_table(&table_start) == FALSE)
+ goto corrupted;
+ } else {
+ /*
+ * Sanity check super block contents - with 0 fragments,
+ * the fragment table should be empty
+ */
+ if(sBlk.s.fragment_table_start != table_start) {
+ ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
+ goto corrupted;
+ }
+ }
+
+ /* Read directory table */
+
+ /* Sanity check super block contents */
+ if(sBlk.s.directory_table_start >= table_start) {
+ ERROR("read_filesystem_tables: directory table start too large in super block\n");
+ goto corrupted;
+ }
if(read_directory_table(sBlk.s.directory_table_start,
- directory_table_end) == FALSE)
- return FALSE;
+ table_start) == FALSE)
+ goto corrupted;
+
+ /* Read inode table */
+
+ /* Sanity check super block contents */
+ if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
+ ERROR("read_filesystem_tables: inode table start too large in super block\n");
+ goto corrupted;
+ }
+
+ if(read_inode_table(sBlk.s.inode_table_start,
+ sBlk.s.directory_table_start) == FALSE)
+ goto corrupted;
if(no_xattrs)
sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK;
return TRUE;
+
+corrupted:
+ ERROR("File system corruption detected\n");
+ return FALSE;
}
--
1.8.3.1