fs/squashfs: sqfs_concat_tokens: check if malloc succeeds
[pandora-u-boot.git] / fs / squashfs / sqfs.c
index f67f7c4..7da2e09 100644 (file)
@@ -154,7 +154,7 @@ static int sqfs_frag_lookup(u32 inode_fragment_index,
        header = get_unaligned_le16(metadata_buffer + table_offset);
        metadata = metadata_buffer + table_offset + SQFS_HEADER_SIZE;
 
-       if (!metadata) {
+       if (!metadata || !header) {
                ret = -ENOMEM;
                goto free_buffer;
        }
@@ -242,6 +242,9 @@ static char *sqfs_concat_tokens(char **token_list, int token_count)
        length = sqfs_get_tokens_length(token_list, token_count);
 
        result = malloc(length + 1);
+       if (!result)
+               return NULL;
+
        result[length] = '\0';
 
        for (i = 0; i < token_count; i++) {
@@ -434,22 +437,28 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
 {
        struct squashfs_super_block *sblk = ctxt.sblk;
        char *path, *target, **sym_tokens, *res, *rem;
-       struct squashfs_ldir_inode *ldir = NULL;
-       int j, ret, new_inode_number, offset;
+       int j, ret = 0, new_inode_number, offset;
        struct squashfs_symlink_inode *sym;
+       struct squashfs_ldir_inode *ldir;
        struct squashfs_dir_inode *dir;
        struct fs_dir_stream *dirsp;
        struct fs_dirent *dent;
        unsigned char *table;
 
+       res = NULL;
+       rem = NULL;
+       path = NULL;
+       target = NULL;
+       sym_tokens = NULL;
+
        dirsp = (struct fs_dir_stream *)dirs;
 
        /* Start by root inode */
        table = sqfs_find_inode(dirs->inode_table, le32_to_cpu(sblk->inodes),
                                sblk->inodes, sblk->block_size);
 
-       /* root is a regular directory, not an extended one */
        dir = (struct squashfs_dir_inode *)table;
+       ldir = (struct squashfs_ldir_inode *)table;
 
        /* get directory offset in directory table */
        offset = sqfs_dir_offset(table, m_list, m_count);
@@ -477,7 +486,8 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
        for (j = 0; j < token_count; j++) {
                if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
                        printf("** Cannot find directory. **\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                while (!sqfs_readdir(dirsp, &dent)) {
@@ -485,11 +495,13 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                        if (!ret)
                                break;
                        free(dirs->entry);
+                       dirs->entry = NULL;
                }
 
                if (ret) {
                        printf("** Cannot find directory. **\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                /* Redefine inode as the found token */
@@ -506,38 +518,63 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                        sym = (struct squashfs_symlink_inode *)table;
                        /* Get first j + 1 tokens */
                        path = sqfs_concat_tokens(token_list, j + 1);
+                       if (!path) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        /* Resolve for these tokens */
                        target = sqfs_resolve_symlink(sym, path);
+                       if (!target) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        /* Join remaining tokens */
                        rem = sqfs_concat_tokens(token_list + j + 1, token_count -
                                                 j - 1);
+                       if (!rem) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        /* Concatenate remaining tokens and symlink's target */
                        res = malloc(strlen(rem) + strlen(target) + 1);
+                       if (!res) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        strcpy(res, target);
                        res[strlen(target)] = '/';
                        strcpy(res + strlen(target) + 1, rem);
                        token_count = sqfs_count_tokens(res);
 
-                       if (token_count < 0)
-                               return -EINVAL;
+                       if (token_count < 0) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
 
                        sym_tokens = malloc(token_count * sizeof(char *));
-                       if (!sym_tokens)
-                               return -EINVAL;
+                       if (!sym_tokens) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
 
                        /* Fill tokens list */
                        ret = sqfs_tokenize(sym_tokens, token_count, res);
-                       if (ret)
-                               return -EINVAL;
+                       if (ret) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
                        free(dirs->entry);
+                       dirs->entry = NULL;
 
                        ret = sqfs_search_dir(dirs, sym_tokens, token_count,
                                              m_list, m_count);
-                       return ret;
+                       goto out;
                } else if (!sqfs_is_dir(get_unaligned_le16(&dir->inode_type))) {
                        printf("** Cannot find directory. **\n");
                        free(dirs->entry);
-                       return -EINVAL;
+                       dirs->entry = NULL;
+                       ret = -EINVAL;
+                       goto out;
                }
 
                /* Check if it is an extended dir. */
@@ -556,7 +593,9 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                if (sqfs_is_empty_dir(table)) {
                        printf("Empty directory.\n");
                        free(dirs->entry);
-                       return SQFS_EMPTY_DIR;
+                       dirs->entry = NULL;
+                       ret = SQFS_EMPTY_DIR;
+                       goto out;
                }
 
                dirs->table += SQFS_DIR_HEADER_SIZE;
@@ -564,6 +603,7 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
                dirs->entry_count = dirs->dir_header->count + 1;
                dirs->size -= SQFS_DIR_HEADER_SIZE;
                free(dirs->entry);
+               dirs->entry = NULL;
        }
 
        offset = sqfs_dir_offset(table, m_list, m_count);
@@ -574,7 +614,13 @@ static int sqfs_search_dir(struct squashfs_dir_stream *dirs, char **token_list,
        else
                memcpy(&dirs->i_ldir, ldir, sizeof(*ldir));
 
-       return 0;
+out:
+       free(res);
+       free(rem);
+       free(path);
+       free(target);
+       free(sym_tokens);
+       return ret;
 }
 
 /*
@@ -688,6 +734,7 @@ static int sqfs_read_inode_table(unsigned char **inode_table)
                                              src_table, src_len);
                        if (ret) {
                                free(*inode_table);
+                               *inode_table = NULL;
                                goto free_itb;
                        }
 
@@ -722,6 +769,8 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
        unsigned long dest_len = 0;
        bool compressed;
 
+       *dir_table = NULL;
+       *pos_list = NULL;
        /* DIRECTORY TABLE */
        table_size = get_unaligned_le64(&sblk->fragment_table_start) -
                get_unaligned_le64(&sblk->directory_table_start);
@@ -736,35 +785,31 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
                return -ENOMEM;
 
        if (sqfs_disk_read(start, n_blks, dtb) < 0)
-               goto free_dtb;
+               goto out;
 
        /* Parse directory table (metadata block) header */
        ret = sqfs_read_metablock(dtb, table_offset, &compressed, &src_len);
        if (ret)
-               goto free_dtb;
+               goto out;
 
        /* Calculate total size to store the whole decompressed table */
        metablks_count = sqfs_count_metablks(dtb, table_offset, table_size);
        if (metablks_count < 1)
-               goto free_dtb;
+               goto out;
 
        *dir_table = malloc(metablks_count * SQFS_METADATA_BLOCK_SIZE);
        if (!*dir_table)
-               goto free_dtb;
+               goto out;
 
        *pos_list = malloc(metablks_count * sizeof(u32));
-       if (!*pos_list) {
-               free(*dir_table);
-               goto free_dtb;
-       }
+       if (!*pos_list)
+               goto out;
 
        ret = sqfs_get_metablk_pos(*pos_list, dtb, table_offset,
                                   metablks_count);
        if (ret) {
                metablks_count = -1;
-               free(*dir_table);
-               free(*pos_list);
-               goto free_dtb;
+               goto out;
        }
 
        src_table = dtb + table_offset + SQFS_HEADER_SIZE;
@@ -780,8 +825,7 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
                                              &dest_len, src_table, src_len);
                        if (ret) {
                                metablks_count = -1;
-                               free(*dir_table);
-                               goto free_dtb;
+                               goto out;
                        }
 
                        if (dest_len < SQFS_METADATA_BLOCK_SIZE) {
@@ -803,7 +847,13 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list)
                src_table += src_len + SQFS_HEADER_SIZE;
        }
 
-free_dtb:
+out:
+       if (metablks_count < 1) {
+               free(*dir_table);
+               free(*pos_list);
+               *dir_table = NULL;
+               *pos_list = NULL;
+       }
        free(dtb);
 
        return metablks_count;
@@ -812,42 +862,57 @@ free_dtb:
 int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
 {
        unsigned char *inode_table = NULL, *dir_table = NULL;
-       int j, token_count, ret = 0, metablks_count;
+       int j, token_count = 0, ret = 0, metablks_count;
        struct squashfs_dir_stream *dirs;
-       char **token_list, *path;
+       char **token_list = NULL, *path = NULL;
        u32 *pos_list = NULL;
 
        dirs = malloc(sizeof(*dirs));
        if (!dirs)
                return -EINVAL;
 
+       /* these should be set to NULL to prevent dangling pointers */
+       dirs->dir_header = NULL;
+       dirs->entry = NULL;
+       dirs->table = NULL;
+       dirs->inode_table = NULL;
+       dirs->dir_table = NULL;
+
        ret = sqfs_read_inode_table(&inode_table);
-       if (ret)
-               return -EINVAL;
+       if (ret) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        metablks_count = sqfs_read_directory_table(&dir_table, &pos_list);
-       if (metablks_count < 1)
-               return -EINVAL;
+       if (metablks_count < 1) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        /* Tokenize filename */
        token_count = sqfs_count_tokens(filename);
-       if (token_count < 0)
-               return -EINVAL;
+       if (token_count < 0) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        path = strdup(filename);
-       if (!path)
-               return -ENOMEM;
+       if (!path) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        token_list = malloc(token_count * sizeof(char *));
        if (!token_list) {
                ret = -EINVAL;
-               goto free_path;
+               goto out;
        }
 
        /* Fill tokens list */
        ret = sqfs_tokenize(token_list, token_count, path);
        if (ret)
-               goto free_tokens;
+               goto out;
        /*
         * ldir's (extended directory) size is greater than dir, so it works as
         * a general solution for the malloc size, since 'i' is a union.
@@ -857,7 +922,7 @@ int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
        ret = sqfs_search_dir(dirs, token_list, token_count, pos_list,
                              metablks_count);
        if (ret)
-               goto free_tokens;
+               goto out;
 
        if (le16_to_cpu(dirs->i_dir.inode_type) == SQFS_DIR_TYPE)
                dirs->size = le16_to_cpu(dirs->i_dir.file_size);
@@ -875,13 +940,16 @@ int sqfs_opendir(const char *filename, struct fs_dir_stream **dirsp)
 
        *dirsp = (struct fs_dir_stream *)dirs;
 
-free_tokens:
+out:
        for (j = 0; j < token_count; j++)
                free(token_list[j]);
        free(token_list);
        free(pos_list);
-free_path:
        free(path);
+       if (ret) {
+               free(inode_table);
+               free(dirs);
+       }
 
        return ret;
 }
@@ -1071,15 +1139,27 @@ static int sqfs_split_path(char **file, char **dir, const char *path)
        char *dirc, *basec, *bname, *dname, *tmp_path;
        int ret = 0;
 
+       *file = NULL;
+       *dir = NULL;
+       dirc = NULL;
+       basec = NULL;
+       bname = NULL;
+       dname = NULL;
+       tmp_path = NULL;
+
        /* check for first slash in path*/
        if (path[0] == '/') {
                tmp_path = strdup(path);
-               if (!tmp_path)
-                       return -ENOMEM;
+               if (!tmp_path) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
        } else {
                tmp_path = malloc(strlen(path) + 2);
-               if (!tmp_path)
-                       return -ENOMEM;
+               if (!tmp_path) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
                tmp_path[0] = '/';
                strcpy(tmp_path + 1, path);
        }
@@ -1088,13 +1168,13 @@ static int sqfs_split_path(char **file, char **dir, const char *path)
        dirc = strdup(tmp_path);
        if (!dirc) {
                ret = -ENOMEM;
-               goto free_tmp;
+               goto out;
        }
 
        basec = strdup(tmp_path);
        if (!basec) {
                ret = -ENOMEM;
-               goto free_dirc;
+               goto out;
        }
 
        dname = sqfs_dirname(dirc);
@@ -1104,14 +1184,14 @@ static int sqfs_split_path(char **file, char **dir, const char *path)
 
        if (!*file) {
                ret = -ENOMEM;
-               goto free_basec;
+               goto out;
        }
 
        if (*dname == '\0') {
                *dir = malloc(2);
                if (!*dir) {
                        ret = -ENOMEM;
-                       goto free_basec;
+                       goto out;
                }
 
                (*dir)[0] = '/';
@@ -1120,15 +1200,19 @@ static int sqfs_split_path(char **file, char **dir, const char *path)
                *dir = strdup(dname);
                if (!*dir) {
                        ret = -ENOMEM;
-                       goto free_basec;
+                       goto out;
                }
        }
 
-free_basec:
+out:
+       if (ret) {
+               free(*file);
+               free(*dir);
+               *dir = NULL;
+               *file = NULL;
+       }
        free(basec);
-free_dirc:
        free(dirc);
-free_tmp:
        free(tmp_path);
 
        return ret;
@@ -1146,7 +1230,10 @@ static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
        finfo->start = get_unaligned_le32(&reg->start_block);
        finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&reg->fragment));
 
-       if (finfo->size < 1 || finfo->offset < 0 || finfo->start < 0)
+       if (finfo->frag && finfo->offset == 0xFFFFFFFF)
+               return -EINVAL;
+
+       if (finfo->size < 1 || finfo->start == 0xFFFFFFFF)
                return -EINVAL;
 
        if (finfo->frag) {
@@ -1156,7 +1243,7 @@ static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg,
                if (ret < 0)
                        return -EINVAL;
                finfo->comp = true;
-               if (fentry->size < 1 || fentry->start < 0)
+               if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
                        return -EINVAL;
        } else {
                datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
@@ -1181,7 +1268,10 @@ static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
        finfo->start = get_unaligned_le64(&lreg->start_block);
        finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(&lreg->fragment));
 
-       if (finfo->size < 1 || finfo->offset < 0 || finfo->start < 0)
+       if (finfo->frag && finfo->offset == 0xFFFFFFFF)
+               return -EINVAL;
+
+       if (finfo->size < 1 || finfo->start == 0x7FFFFFFF)
                return -EINVAL;
 
        if (finfo->frag) {
@@ -1191,7 +1281,7 @@ static int sqfs_get_lregfile_info(struct squashfs_lreg_inode *lreg,
                if (ret < 0)
                        return -EINVAL;
                finfo->comp = true;
-               if (fentry->size < 1 || fentry->start < 0)
+               if (fentry->size < 1 || fentry->start == 0x7FFFFFFF)
                        return -EINVAL;
        } else {
                datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz));
@@ -1549,4 +1639,5 @@ void sqfs_closedir(struct fs_dir_stream *dirs)
        free(sqfs_dirs->inode_table);
        free(sqfs_dirs->dir_table);
        free(sqfs_dirs->dir_header);
+       free(sqfs_dirs);
 }