Path: blob/master/Utilities/cmliblzma/liblzma/common/index_hash.c
3153 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file index_hash.c5/// \brief Validates Index by using a hash function6//7// Author: Lasse Collin8//9///////////////////////////////////////////////////////////////////////////////1011#include "common.h"12#include "index.h"13#include "check.h"141516typedef struct {17/// Sum of the Block sizes (including Block Padding)18lzma_vli blocks_size;1920/// Sum of the Uncompressed Size fields21lzma_vli uncompressed_size;2223/// Number of Records24lzma_vli count;2526/// Size of the List of Index Records as bytes27lzma_vli index_list_size;2829/// Check calculated from Unpadded Sizes and Uncompressed Sizes.30lzma_check_state check;3132} lzma_index_hash_info;333435struct lzma_index_hash_s {36enum {37SEQ_BLOCK,38SEQ_COUNT,39SEQ_UNPADDED,40SEQ_UNCOMPRESSED,41SEQ_PADDING_INIT,42SEQ_PADDING,43SEQ_CRC32,44} sequence;4546/// Information collected while decoding the actual Blocks.47lzma_index_hash_info blocks;4849/// Information collected from the Index field.50lzma_index_hash_info records;5152/// Number of Records not fully decoded53lzma_vli remaining;5455/// Unpadded Size currently being read from an Index Record.56lzma_vli unpadded_size;5758/// Uncompressed Size currently being read from an Index Record.59lzma_vli uncompressed_size;6061/// Position in variable-length integers when decoding them from62/// the List of Records.63size_t pos;6465/// CRC32 of the Index66uint32_t crc32;67};686970extern LZMA_API(lzma_index_hash *)71lzma_index_hash_init(lzma_index_hash *index_hash,72const lzma_allocator *allocator)73{74if (index_hash == NULL) {75index_hash = lzma_alloc(sizeof(lzma_index_hash), allocator);76if (index_hash == NULL)77return NULL;78}7980index_hash->sequence = SEQ_BLOCK;81index_hash->blocks.blocks_size = 0;82index_hash->blocks.uncompressed_size = 0;83index_hash->blocks.count = 0;84index_hash->blocks.index_list_size = 0;85index_hash->records.blocks_size = 0;86index_hash->records.uncompressed_size = 0;87index_hash->records.count = 0;88index_hash->records.index_list_size = 0;89index_hash->unpadded_size = 0;90index_hash->uncompressed_size = 0;91index_hash->pos = 0;92index_hash->crc32 = 0;9394// These cannot fail because LZMA_CHECK_BEST is known to be supported.95(void)lzma_check_init(&index_hash->blocks.check, LZMA_CHECK_BEST);96(void)lzma_check_init(&index_hash->records.check, LZMA_CHECK_BEST);9798return index_hash;99}100101102extern LZMA_API(void)103lzma_index_hash_end(lzma_index_hash *index_hash,104const lzma_allocator *allocator)105{106lzma_free(index_hash, allocator);107return;108}109110111extern LZMA_API(lzma_vli)112lzma_index_hash_size(const lzma_index_hash *index_hash)113{114// Get the size of the Index from ->blocks instead of ->records for115// cases where application wants to know the Index Size before116// decoding the Index.117return index_size(index_hash->blocks.count,118index_hash->blocks.index_list_size);119}120121122/// Updates the sizes and the hash without any validation.123static void124hash_append(lzma_index_hash_info *info, lzma_vli unpadded_size,125lzma_vli uncompressed_size)126{127info->blocks_size += vli_ceil4(unpadded_size);128info->uncompressed_size += uncompressed_size;129info->index_list_size += lzma_vli_size(unpadded_size)130+ lzma_vli_size(uncompressed_size);131++info->count;132133const lzma_vli sizes[2] = { unpadded_size, uncompressed_size };134lzma_check_update(&info->check, LZMA_CHECK_BEST,135(const uint8_t *)(sizes), sizeof(sizes));136137return;138}139140141extern LZMA_API(lzma_ret)142lzma_index_hash_append(lzma_index_hash *index_hash, lzma_vli unpadded_size,143lzma_vli uncompressed_size)144{145// Validate the arguments.146if (index_hash == NULL || index_hash->sequence != SEQ_BLOCK147|| unpadded_size < UNPADDED_SIZE_MIN148|| unpadded_size > UNPADDED_SIZE_MAX149|| uncompressed_size > LZMA_VLI_MAX)150return LZMA_PROG_ERROR;151152// Update the hash.153hash_append(&index_hash->blocks, unpadded_size, uncompressed_size);154155// Validate the properties of *info are still in allowed limits.156if (index_hash->blocks.blocks_size > LZMA_VLI_MAX157|| index_hash->blocks.uncompressed_size > LZMA_VLI_MAX158|| index_size(index_hash->blocks.count,159index_hash->blocks.index_list_size)160> LZMA_BACKWARD_SIZE_MAX161|| index_stream_size(index_hash->blocks.blocks_size,162index_hash->blocks.count,163index_hash->blocks.index_list_size)164> LZMA_VLI_MAX)165return LZMA_DATA_ERROR;166167return LZMA_OK;168}169170171extern LZMA_API(lzma_ret)172lzma_index_hash_decode(lzma_index_hash *index_hash, const uint8_t *in,173size_t *in_pos, size_t in_size)174{175// Catch zero input buffer here, because in contrast to Index encoder176// and decoder functions, applications call this function directly177// instead of via lzma_code(), which does the buffer checking.178if (*in_pos >= in_size)179return LZMA_BUF_ERROR;180181// NOTE: This function has many similarities to index_encode() and182// index_decode() functions found from index_encoder.c and183// index_decoder.c. See the comments especially in index_encoder.c.184const size_t in_start = *in_pos;185lzma_ret ret = LZMA_OK;186187while (*in_pos < in_size)188switch (index_hash->sequence) {189case SEQ_BLOCK:190// Check the Index Indicator is present.191if (in[(*in_pos)++] != INDEX_INDICATOR)192return LZMA_DATA_ERROR;193194index_hash->sequence = SEQ_COUNT;195break;196197case SEQ_COUNT: {198ret = lzma_vli_decode(&index_hash->remaining,199&index_hash->pos, in, in_pos, in_size);200if (ret != LZMA_STREAM_END)201goto out;202203// The count must match the count of the Blocks decoded.204if (index_hash->remaining != index_hash->blocks.count)205return LZMA_DATA_ERROR;206207ret = LZMA_OK;208index_hash->pos = 0;209210// Handle the special case when there are no Blocks.211index_hash->sequence = index_hash->remaining == 0212? SEQ_PADDING_INIT : SEQ_UNPADDED;213break;214}215216case SEQ_UNPADDED:217case SEQ_UNCOMPRESSED: {218lzma_vli *size = index_hash->sequence == SEQ_UNPADDED219? &index_hash->unpadded_size220: &index_hash->uncompressed_size;221222ret = lzma_vli_decode(size, &index_hash->pos,223in, in_pos, in_size);224if (ret != LZMA_STREAM_END)225goto out;226227ret = LZMA_OK;228index_hash->pos = 0;229230if (index_hash->sequence == SEQ_UNPADDED) {231if (index_hash->unpadded_size < UNPADDED_SIZE_MIN232|| index_hash->unpadded_size233> UNPADDED_SIZE_MAX)234return LZMA_DATA_ERROR;235236index_hash->sequence = SEQ_UNCOMPRESSED;237} else {238// Update the hash.239hash_append(&index_hash->records,240index_hash->unpadded_size,241index_hash->uncompressed_size);242243// Verify that we don't go over the known sizes. Note244// that this validation is simpler than the one used245// in lzma_index_hash_append(), because here we know246// that values in index_hash->blocks are already247// validated and we are fine as long as we don't248// exceed them in index_hash->records.249if (index_hash->blocks.blocks_size250< index_hash->records.blocks_size251|| index_hash->blocks.uncompressed_size252< index_hash->records.uncompressed_size253|| index_hash->blocks.index_list_size254< index_hash->records.index_list_size)255return LZMA_DATA_ERROR;256257// Check if this was the last Record.258index_hash->sequence = --index_hash->remaining == 0259? SEQ_PADDING_INIT : SEQ_UNPADDED;260}261262break;263}264265case SEQ_PADDING_INIT:266index_hash->pos = (LZMA_VLI_C(4) - index_size_unpadded(267index_hash->records.count,268index_hash->records.index_list_size)) & 3;269index_hash->sequence = SEQ_PADDING;270271// Fall through272273case SEQ_PADDING:274if (index_hash->pos > 0) {275--index_hash->pos;276if (in[(*in_pos)++] != 0x00)277return LZMA_DATA_ERROR;278279break;280}281282// Compare the sizes.283if (index_hash->blocks.blocks_size284!= index_hash->records.blocks_size285|| index_hash->blocks.uncompressed_size286!= index_hash->records.uncompressed_size287|| index_hash->blocks.index_list_size288!= index_hash->records.index_list_size)289return LZMA_DATA_ERROR;290291// Finish the hashes and compare them.292lzma_check_finish(&index_hash->blocks.check, LZMA_CHECK_BEST);293lzma_check_finish(&index_hash->records.check, LZMA_CHECK_BEST);294if (memcmp(index_hash->blocks.check.buffer.u8,295index_hash->records.check.buffer.u8,296lzma_check_size(LZMA_CHECK_BEST)) != 0)297return LZMA_DATA_ERROR;298299// Finish the CRC32 calculation.300index_hash->crc32 = lzma_crc32(in + in_start,301*in_pos - in_start, index_hash->crc32);302303index_hash->sequence = SEQ_CRC32;304305// Fall through306307case SEQ_CRC32:308do {309if (*in_pos == in_size)310return LZMA_OK;311312if (((index_hash->crc32 >> (index_hash->pos * 8))313& 0xFF) != in[(*in_pos)++]) {314#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION315return LZMA_DATA_ERROR;316#endif317}318319} while (++index_hash->pos < 4);320321return LZMA_STREAM_END;322323default:324assert(0);325return LZMA_PROG_ERROR;326}327328out:329// Update the CRC32.330//331// Avoid null pointer + 0 (undefined behavior) in "in + in_start".332// In such a case we had no input and thus in_used == 0.333{334const size_t in_used = *in_pos - in_start;335if (in_used > 0)336index_hash->crc32 = lzma_crc32(in + in_start,337in_used, index_hash->crc32);338}339340return ret;341}342343344