From c9875a5fe85901b272c8e7db1b9bce513f02f1af Mon Sep 17 00:00:00 2001 From: Joao Marcos Costa Date: Wed, 19 Aug 2020 18:28:41 +0200 Subject: [PATCH] fs/squashfs: Fix Coverity Scan defects Fix defects such as uninitialized variables and untrusted pointer operations. Most part of the tainted variables and the related defects actually comes from Linux's macro get_unaligned_le**, extensively used in SquashFS code. Add sanity checks for those variables. Signed-off-by: Joao Marcos Costa --- fs/squashfs/sqfs.c | 40 +++++++++++++++++++++++++++++----------- fs/squashfs/sqfs_dir.c | 3 +++ fs/squashfs/sqfs_inode.c | 5 ++++- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/fs/squashfs/sqfs.c b/fs/squashfs/sqfs.c index 9bd7b98d88..f67f7c4a40 100644 --- a/fs/squashfs/sqfs.c +++ b/fs/squashfs/sqfs.c @@ -154,6 +154,11 @@ 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) { + ret = -ENOMEM; + goto free_buffer; + } + entries = malloc(SQFS_METADATA_BLOCK_SIZE); if (!entries) { ret = -ENOMEM; @@ -272,8 +277,8 @@ static int sqfs_join(char **strings, char *dest, int start, int end, */ static int sqfs_tokenize(char **tokens, int count, const char *str) { + int i, j, ret = 0; char *aux, *strc; - int i, j; strc = strdup(str); if (!strc) @@ -282,8 +287,8 @@ static int sqfs_tokenize(char **tokens, int count, const char *str) if (!strcmp(strc, "/")) { tokens[0] = strdup(strc); if (!tokens[0]) { - free(strc); - return -ENOMEM; + ret = -ENOMEM; + goto free_strc; } } else { for (j = 0; j < count; j++) { @@ -292,15 +297,16 @@ static int sqfs_tokenize(char **tokens, int count, const char *str) if (!tokens[j]) { for (i = 0; i < j; i++) free(tokens[i]); - free(strc); - return -ENOMEM; + ret = -ENOMEM; + goto free_strc; } } } +free_strc: free(strc); - return 0; + return ret; } /* @@ -428,9 +434,9 @@ 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; struct squashfs_symlink_inode *sym; - struct squashfs_ldir_inode *ldir; struct squashfs_dir_inode *dir; struct fs_dir_stream *dirsp; struct fs_dirent *dent; @@ -630,7 +636,7 @@ static int sqfs_read_inode_table(unsigned char **inode_table) int j, ret = 0, metablks_count; unsigned char *src_table, *itb; u32 src_len, dest_offset = 0; - unsigned long dest_len; + unsigned long dest_len = 0; bool compressed; table_size = get_unaligned_le64(&sblk->directory_table_start) - @@ -685,6 +691,7 @@ static int sqfs_read_inode_table(unsigned char **inode_table) goto free_itb; } + dest_offset += dest_len; } else { memcpy(*inode_table + (j * SQFS_METADATA_BLOCK_SIZE), src_table, src_len); @@ -694,7 +701,7 @@ static int sqfs_read_inode_table(unsigned char **inode_table) * Offsets to the decompression destination, to the metadata * buffer 'itb' and to the decompression source, respectively. */ - dest_offset += dest_len; + table_offset += src_len + SQFS_HEADER_SIZE; src_table += src_len + SQFS_HEADER_SIZE; } @@ -712,7 +719,7 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list) int j, ret = 0, metablks_count = -1; unsigned char *src_table, *dtb; u32 src_len, dest_offset = 0; - unsigned long dest_len; + unsigned long dest_len = 0; bool compressed; /* DIRECTORY TABLE */ @@ -781,6 +788,8 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list) dest_offset += dest_len; break; } + + dest_offset += dest_len; } else { memcpy(*dir_table + (j * SQFS_METADATA_BLOCK_SIZE), src_table, src_len); @@ -790,7 +799,6 @@ static int sqfs_read_directory_table(unsigned char **dir_table, u32 **pos_list) * Offsets to the decompression destination, to the metadata * buffer 'dtb' and to the decompression source, respectively. */ - dest_offset += dest_len; table_offset += src_len + SQFS_HEADER_SIZE; src_table += src_len + SQFS_HEADER_SIZE; } @@ -1138,6 +1146,9 @@ static int sqfs_get_regfile_info(struct squashfs_reg_inode *reg, finfo->start = get_unaligned_le32(®->start_block); finfo->frag = SQFS_IS_FRAGMENTED(get_unaligned_le32(®->fragment)); + if (finfo->size < 1 || finfo->offset < 0 || finfo->start < 0) + return -EINVAL; + if (finfo->frag) { datablk_count = finfo->size / le32_to_cpu(blksz); ret = sqfs_frag_lookup(get_unaligned_le32(®->fragment), @@ -1145,6 +1156,8 @@ 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) + return -EINVAL; } else { datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz)); } @@ -1168,6 +1181,9 @@ 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) + return -EINVAL; + if (finfo->frag) { datablk_count = finfo->size / le32_to_cpu(blksz); ret = sqfs_frag_lookup(get_unaligned_le32(&lreg->fragment), @@ -1175,6 +1191,8 @@ 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) + return -EINVAL; } else { datablk_count = DIV_ROUND_UP(finfo->size, le32_to_cpu(blksz)); } diff --git a/fs/squashfs/sqfs_dir.c b/fs/squashfs/sqfs_dir.c index 5f7660aeae..00d2891e7d 100644 --- a/fs/squashfs/sqfs_dir.c +++ b/fs/squashfs/sqfs_dir.c @@ -53,6 +53,9 @@ int sqfs_dir_offset(void *dir_i, u32 *m_list, int m_count) return -EINVAL; } + if (offset < 0) + return -EINVAL; + for (j = 0; j < m_count; j++) { if (m_list[j] == start_block) return (++j * SQFS_METADATA_BLOCK_SIZE) + offset; diff --git a/fs/squashfs/sqfs_inode.c b/fs/squashfs/sqfs_inode.c index b037a9b2ac..1387779a85 100644 --- a/fs/squashfs/sqfs_inode.c +++ b/fs/squashfs/sqfs_inode.c @@ -138,11 +138,14 @@ void *sqfs_find_inode(void *inode_table, int inode_number, __le32 inode_count, int sqfs_read_metablock(unsigned char *file_mapping, int offset, bool *compressed, u32 *data_size) { - unsigned char *data; + const unsigned char *data; u16 header; data = file_mapping + offset; header = get_unaligned((u16 *)data); + if (!header || !data) + return -EINVAL; + *compressed = SQFS_COMPRESSED_METADATA(header); *data_size = SQFS_METADATA_SIZE(header);