@@ -29,30 +29,63 @@
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;
int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk.s.fragments);
int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk.s.fragments);
long long fragment_table_index[indexes];
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: %d fragments, reading %d 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 = alloc_index_table(indexes);
fragment_table = malloc(bytes);
if(fragment_table == NULL)
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");
@@ -78,7 +111,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;
}

@@ -356,25 +389,42 @@ struct dir *squashfs_opendir_4(unsigned int block_start, unsigned int offset,
}


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;

/*
* 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_uids_guids: no_ids %d\n", sBlk.s.no_ids);
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);
@@ -393,7 +443,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;
}
@@ -407,12 +457,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;
@@ -432,30 +500,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;
}
@@ -2465,7 +2465,7 @@ int parse_number(char *arg, int *res)


#define VERSION() \
printf("unsquashfs version 4.3-git (2019/07/05)\n");\
printf("unsquashfs version 4.3-git (2019/07/15)\n");\
printf("copyright (C) 2019 Phillip Lougher "\
"<phillip@squashfs.org.uk>\n\n");\
printf("This program is free software; you can redistribute it and/or"\