diff -Nupr a/squashfs-tools/Makefile b/squashfs-tools/Makefile --- a/squashfs-tools/Makefile 2023-06-26 23:57:01.519732066 -0500 +++ b/squashfs-tools/Makefile 2023-06-26 23:57:52.192705923 -0500 @@ -117,7 +117,8 @@ MKSQUASHFS_OBJS = mksquashfs.o read_fs.o caches-queues-lists.o UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \ - unsquash-4.o unsquash-1234.o swap.o compressor.o unsquashfs_info.o + unsquash-4.o unsquash-1234.o unsquash-12.o swap.o compressor.o \ + unsquashfs_info.o CFLAGS ?= -O2 CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \ @@ -294,6 +295,8 @@ unsquash-4.o: unsquashfs.h unsquash-4.c unsquash-1234.o: unsquash-1234.c +unsquash-12.o: unsquash-12.c unsquashfs.h + unsquashfs_xattr.o: unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h unsquashfs_info.o: unsquashfs.h squashfs_fs.h diff -Nupr a/squashfs-tools/unsquash-1234.c b/squashfs-tools/unsquash-1234.c --- a/squashfs-tools/unsquash-1234.c 2023-06-26 23:57:01.519732066 -0500 +++ b/squashfs-tools/unsquash-1234.c 2023-06-26 23:57:52.192705923 -0500 @@ -25,6 +25,8 @@ * unsquash-4. */ +#include "unsquashfs.h" + #define TRUE 1 #define FALSE 0 /* @@ -56,3 +58,23 @@ int check_name(char *name, int size) return TRUE; } + +/* + * Check directory for duplicate names. As the directory should be sorted, + * duplicates will be consecutive. Obviously we also need to check if the + * directory has been deliberately unsorted, to evade this check. + */ +int check_directory(struct dir *dir) +{ + int i; + struct dir_ent *ent; + + if (dir->dir_count < 2) + return TRUE; + + for (ent = dir->dirs, i = 0; i < dir->dir_count - 1; ent = ent->next, i++) + if (strcmp(ent->name, ent->next->name) >= 0) + return FALSE; + + return TRUE; +} diff -Nupr a/squashfs-tools/unsquash-12.c b/squashfs-tools/unsquash-12.c --- a/squashfs-tools/unsquash-12.c 1969-12-31 18:00:00.000000000 -0600 +++ b/squashfs-tools/unsquash-12.c 2023-06-26 23:57:52.192705923 -0500 @@ -0,0 +1,103 @@ +/* + * Unsquash a squashfs filesystem. This is a highly compressed read only + * filesystem. + * + * Copyright (c) 2021 + * Phillip Lougher + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * unsquash-12.c + * + * Helper functions used by unsquash-1 and unsquash-2. + */ + +#include "unsquashfs.h" + +/* + * Bottom up linked list merge sort. + * + */ +void sort_directory(struct dir *dir) +{ + struct dir_ent *cur, *l1, *l2, *next; + int len1, len2, stride = 1; + + if (dir->dir_count < 2) + return; + + /* + * We can consider our linked-list to be made up of stride length + * sublists. Each iteration around this loop merges adjacent + * stride length sublists into larger 2*stride sublists. We stop + * when stride becomes equal to the entire list. + * + * Initially stride = 1 (by definition a sublist of 1 is sorted), and + * these 1 element sublists are merged into 2 element sublists, which + * are then merged into 4 element sublists and so on. + */ + do { + l2 = dir->dirs; /* head of current linked list */ + cur = NULL; /* empty output list */ + + /* + * Iterate through the linked list, merging adjacent sublists. + * On each interation l2 points to the next sublist pair to be + * merged (if there's only one sublist left this is simply added + * to the output list) + */ + while (l2) { + l1 = l2; + for (len1 = 0; l2 && len1 < stride; len1 ++, l2 = l2->next); + len2 = stride; + + /* + * l1 points to first sublist. + * l2 points to second sublist. + * Merge them onto the output list + */ + while (len1 && l2 && len2) { + if(strcmp(l1->name, l2->name) <= 0) { + next = l1; + l1 = l1->next; + len1 --; + } else { + next = l2; + l2 = l2->next; + len2 --; + } + + if (cur) { + cur->next = next; + cur = next; + } else + dir->dirs = cur = next; + } + /* + * One sublist is now empty, copy the other one onto the + * output list + */ + for (; len1; len1 --, l1 = l1->next) { + if(cur) { + cur->next = l1; + cur = l1; + } else + dir->dirs = cur = l1; + } + } + cur->next = NULL; + stride = stride << 1; + } while(stride < dir->dir_count); +} diff -Nupr a/squashfs-tools/unsquash-1.c b/squashfs-tools/unsquash-1.c --- a/squashfs-tools/unsquash-1.c 2023-06-26 23:57:01.519732066 -0500 +++ b/squashfs-tools/unsquash-1.c 2023-06-26 23:57:52.192705923 -0500 @@ -211,7 +211,7 @@ struct dir *squashfs_opendir_1(unsigned long long start; int bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -224,7 +224,7 @@ struct dir *squashfs_opendir_1(unsigned EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -295,29 +295,37 @@ struct dir *squashfs_opendir_1(unsigned TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if (ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + /* check directory for duplicate names. Need to sort directory first */ + sort_directory(dir); + if (check_directory(dir) == FALSE) { + ERROR("File system corrupted: directory has duplicate names\n"); + goto corrupted; + } + return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquash-3.c b/squashfs-tools/unsquash-3.c --- a/squashfs-tools/unsquash-3.c 2023-06-26 23:57:01.519732066 -0500 +++ b/squashfs-tools/unsquash-3.c 2023-06-26 23:57:52.192705923 -0500 @@ -288,7 +288,7 @@ struct dir *squashfs_opendir_3(unsigned long long start; int bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -301,7 +301,7 @@ struct dir *squashfs_opendir_3(unsigned EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -373,28 +373,35 @@ struct dir *squashfs_opendir_3(unsigned TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if (ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + /* check directory for duplicate names and sorting */ + if (check_directory(dir) == FALSE) { + ERROR("File system corrupted: directory has duplicate names or is unsorted\n"); + goto corrupted; + } + return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquash-4.c b/squashfs-tools/unsquash-4.c --- a/squashfs-tools/unsquash-4.c 2023-06-26 23:57:01.519732066 -0500 +++ b/squashfs-tools/unsquash-4.c 2023-06-26 23:57:52.193705923 -0500 @@ -268,7 +268,7 @@ struct dir *squashfs_opendir_4(unsigned long long start; int bytes; int dir_count, size; - struct dir_ent *new_dir; + struct dir_ent *ent, *cur_ent = NULL; struct dir *dir; TRACE("squashfs_opendir: inode start block %d, offset %d\n", @@ -281,7 +281,7 @@ struct dir *squashfs_opendir_4(unsigned EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n"); dir->dir_count = 0; - dir->cur_entry = 0; + dir->cur_entry = NULL; dir->mode = (*i)->mode; dir->uid = (*i)->uid; dir->guid = (*i)->gid; @@ -342,29 +342,36 @@ struct dir *squashfs_opendir_4(unsigned TRACE("squashfs_opendir: directory entry %s, inode " "%d:%d, type %d\n", dire->name, dirh.start_block, dire->offset, dire->type); - if((dir->dir_count % DIR_ENT_SIZE) == 0) { - new_dir = realloc(dir->dirs, (dir->dir_count + - DIR_ENT_SIZE) * sizeof(struct dir_ent)); - if(new_dir == NULL) - EXIT_UNSQUASH("squashfs_opendir: " - "realloc failed!\n"); - dir->dirs = new_dir; - } - strcpy(dir->dirs[dir->dir_count].name, dire->name); - dir->dirs[dir->dir_count].start_block = - dirh.start_block; - dir->dirs[dir->dir_count].offset = dire->offset; - dir->dirs[dir->dir_count].type = dire->type; + + ent = malloc(sizeof(struct dir_ent)); + if (ent == NULL) + MEM_ERROR(); + + ent->name = strdup(dire->name); + ent->start_block = dirh.start_block; + ent->offset = dire->offset; + ent->type = dire->type; + ent->next = NULL; + if (cur_ent == NULL) + dir->dirs = ent; + else + cur_ent->next = ent; + cur_ent = ent; dir->dir_count ++; bytes += dire->size + 1; } } + /* check directory for duplicate names and sorting */ + if (check_directory(dir) == FALSE) { + ERROR("File system corrupted: directory has duplicate names or is unsorted\n"); + goto corrupted; + } + return dir; corrupted: - free(dir->dirs); - free(dir); + squashfs_closedir(dir); return NULL; } diff -Nupr a/squashfs-tools/unsquashfs.c b/squashfs-tools/unsquashfs.c --- a/squashfs-tools/unsquashfs.c 2023-06-26 23:57:01.520732065 -0500 +++ b/squashfs-tools/unsquashfs.c 2023-06-26 23:57:52.193705923 -0500 @@ -1231,14 +1231,18 @@ failed: int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block, unsigned int *offset, unsigned int *type) { - if(dir->cur_entry == dir->dir_count) + if (dir->cur_entry == NULL) + dir->cur_entry = dir->dirs; + else + dir->cur_entry = dir->cur_entry->next; + + if (dir->cur_entry == NULL) return FALSE; - *name = dir->dirs[dir->cur_entry].name; - *start_block = dir->dirs[dir->cur_entry].start_block; - *offset = dir->dirs[dir->cur_entry].offset; - *type = dir->dirs[dir->cur_entry].type; - dir->cur_entry ++; + *name = dir->cur_entry->name; + *start_block = dir->cur_entry->start_block; + *offset = dir->cur_entry->offset; + *type = dir->cur_entry->type; return TRUE; } @@ -1246,7 +1250,16 @@ unsigned int *offset, unsigned int *type void squashfs_closedir(struct dir *dir) { - free(dir->dirs); + struct dir_ent *ent = dir->dirs; + + while (ent) { + struct dir_ent *tmp = ent; + + ent = ent->next; + free(tmp->name); + free(tmp); + } + free(dir); } @@ -1895,6 +1908,7 @@ int read_super(char *source) * 1.x, 2.x and 3.x filesystems use gzip compression. */ comp = lookup_compressor("gzip"); + return TRUE; failed_mount: diff -Nupr a/squashfs-tools/unsquashfs.h b/squashfs-tools/unsquashfs.h --- a/squashfs-tools/unsquashfs.h 2023-06-26 23:57:01.520732065 -0500 +++ b/squashfs-tools/unsquashfs.h 2023-06-26 23:57:52.193705923 -0500 @@ -168,21 +168,22 @@ struct queue { #define DIR_ENT_SIZE 16 struct dir_ent { - char name[SQUASHFS_NAME_LEN + 1]; + char *name; unsigned int start_block; unsigned int offset; unsigned int type; + struct dir_ent *next; }; struct dir { int dir_count; - int cur_entry; unsigned int mode; uid_t uid; gid_t guid; unsigned int mtime; unsigned int xattr; struct dir_ent *dirs; + struct dir_ent *cur_entry; }; struct file_entry { @@ -278,4 +279,10 @@ extern int read_uids_guids_4(); /* unsquash-1234.c */ extern int check_name(char *, int); + +extern void squashfs_closedir(struct dir *); +extern int check_directory(struct dir *); + +/* unsquash-12.c */ +extern void sort_directory(struct dir *); #endif