diff --git a/encoder/basisu_comp.cpp b/encoder/basisu_comp.cpp index acbedc3..dee91aa 100644 --- a/encoder/basisu_comp.cpp +++ b/encoder/basisu_comp.cpp @@ -2146,8 +2146,8 @@ namespace basisu if ((m_params.m_resample_width > 0) && (m_params.m_resample_height > 0)) { - int new_width = basisu::minimum(m_params.m_resample_width, BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); - int new_height = basisu::minimum(m_params.m_resample_height, BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + int new_width = basisu::minimum(m_params.m_resample_width, basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + int new_height = basisu::minimum(m_params.m_resample_height, basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); debug_printf("Resampling to %ix%i\n", new_width, new_height); @@ -2171,8 +2171,8 @@ namespace basisu // TODO: A box filter - kaiser looks too sharp on video. Let the caller control this. if (m_params.m_hdr) { - int new_width = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image_hdr.get_width() * m_params.m_resample_factor)), BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); - int new_height = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image_hdr.get_height() * m_params.m_resample_factor)), BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + int new_width = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image_hdr.get_width() * m_params.m_resample_factor)), basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + int new_height = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image_hdr.get_height() * m_params.m_resample_factor)), basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); debug_printf("Resampling to %ix%i\n", new_width, new_height); @@ -2183,8 +2183,8 @@ namespace basisu } else { - int new_width = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image.get_width() * m_params.m_resample_factor)), BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); - int new_height = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image.get_height() * m_params.m_resample_factor)), BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + int new_width = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image.get_width() * m_params.m_resample_factor)), basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); + int new_height = basisu::minimum(basisu::maximum(1, (int)ceilf(file_image.get_height() * m_params.m_resample_factor)), basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION); debug_printf("Resampling to %ix%i\n", new_width, new_height); @@ -2203,7 +2203,7 @@ namespace basisu return false; } - if ((width > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) + if ((width > basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) { error_printf("basis_compressor::read_source_images: Source image \"%s\" is too large!\n", pSource_filename); return false; @@ -5271,7 +5271,7 @@ namespace basisu *pSize = 0; - if ((width > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) + if ((width > basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) { error_printf("basis_compress: Image too large\n"); return nullptr; @@ -5305,7 +5305,7 @@ namespace basisu *pSize = 0; - if ((width > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) + if ((width > basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (height > basist::BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) { error_printf("basis_compress: Image too large\n"); return nullptr; diff --git a/encoder/basisu_comp.h b/encoder/basisu_comp.h index f03bf59..fd0de37 100644 --- a/encoder/basisu_comp.h +++ b/encoder/basisu_comp.h @@ -40,9 +40,7 @@ namespace basisu { struct opencl_context; typedef opencl_context* opencl_context_ptr; - - const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384; - + // Allow block's color distance to increase by 1.5 while searching for an alternative nearby endpoint. const float BASISU_DEFAULT_ENDPOINT_RDO_THRESH = 1.5f; diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp index f2fa623..798f000 100644 --- a/transcoder/basisu_transcoder.cpp +++ b/transcoder/basisu_transcoder.cpp @@ -8593,12 +8593,20 @@ namespace basist uint32_t* pPVRTC_endpoints = nullptr; if ((fmt == block_format::cPVRTC1_4_RGB) || (fmt == block_format::cPVRTC1_4_RGBA)) { - pPVRTC_work_mem = malloc(num_blocks_x * num_blocks_y * (sizeof(decoder_etc_block) + sizeof(uint32_t))); + const uint64_t alloc_size = (uint64_t)num_blocks_x * (uint64_t)num_blocks_y * (sizeof(decoder_etc_block) + sizeof(uint32_t)); + if (alloc_size > (256u * 1024u * 1024u)) // sanity check, 16384x16384=192MB + { + BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: malloc would be too large\n"); + return false; + } + + pPVRTC_work_mem = malloc(alloc_size); if (!pPVRTC_work_mem) { BASISU_DEVEL_ERROR("basisu_lowlevel_etc1s_transcoder::transcode_slice: malloc failed\n"); return false; } + pPVRTC_endpoints = (uint32_t*)&((decoder_etc_block*)pPVRTC_work_mem)[num_blocks_x * num_blocks_y]; } @@ -11984,6 +11992,44 @@ namespace basist return (basis_tex_format)(uint32_t)pHeader->m_tex_format; } + static bool check_slice_desc(const basis_slice_desc& slice_desc, uint32_t block_width, uint32_t block_height) + { + // calculated by us from the source format + assert((block_width >= 4) && (block_width <= 12)); + assert((block_height >= 4) && (block_height <= 12)); + + // ensure the unpadded image dimensions are valid + if ((!slice_desc.m_orig_width) || (!slice_desc.m_orig_height)) + return false; + + if ((slice_desc.m_orig_width > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION) || (slice_desc.m_orig_height > BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION)) + return false; + + // now calculate the number of expected blocks on each dimension given the source format's block size + const uint32_t num_blocks_x_expected = (slice_desc.m_orig_width + block_width - 1) / block_width; + const uint32_t num_blocks_y_expected = (slice_desc.m_orig_height + block_height - 1) / block_height; + + // ensure the slice has the exact number of expected blocks on each dimension + if ((slice_desc.m_num_blocks_x != num_blocks_x_expected) || (slice_desc.m_num_blocks_y != num_blocks_y_expected)) + return false; + + // at this point the original texture dimensions are valid, and the stored block dimensions + // exactly match the padded dimensions implied by those original dimensions + + return true; + } + + static bool check_slice_desc(const basis_file_header* pHeader, const basis_slice_desc& slice_desc) + { + if (pHeader->m_tex_format >= (uint32_t)basis_tex_format::cTotalFormats) + return false; + + const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format)); + const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format)); + + return check_slice_desc(slice_desc, block_width, block_height); + } + bool basisu_transcoder::get_image_info(const void* pData, uint32_t data_size, basisu_image_info& image_info, uint32_t image_index) const { if (!validate_header_quick(pData, data_size)) @@ -12023,12 +12069,18 @@ namespace basist } const basis_slice_desc& slice_desc = pSlice_descs[slice_index]; - + image_info.m_image_index = image_index; image_info.m_total_levels = total_levels; image_info.m_alpha_flag = false; + if (pHeader->m_tex_format >= (uint32_t)basis_tex_format::cTotalFormats) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid m_tex_format\n"); + return false; + } + // For ETC1S, if anything has alpha all images have alpha. For UASTC, we only report alpha when the image actually has alpha. if (pHeader->m_tex_format == (int)basis_tex_format::cETC1S) image_info.m_alpha_flag = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0; @@ -12039,6 +12091,12 @@ namespace basist const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format)); const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format)); + + if (!check_slice_desc(slice_desc, block_width, block_height)) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid slice_desc\n"); + return false; + } image_info.m_width = slice_desc.m_num_blocks_x * block_width; image_info.m_height = slice_desc.m_num_blocks_y * block_height; @@ -12119,10 +12177,25 @@ namespace basist return false; } + if (pHeader->m_tex_format >= (uint32_t)basis_tex_format::cTotalFormats) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid m_tex_format\n"); + return false; + } + const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs); const basis_slice_desc& slice_desc = pSlice_descs[slice_index]; + const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format)); + const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format)); + + if (!check_slice_desc(slice_desc, block_width, block_height)) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_desc: invalid slice_desc\n"); + return false; + } + orig_width = slice_desc.m_orig_width; orig_height = slice_desc.m_orig_height; total_blocks = slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y; @@ -12153,6 +12226,12 @@ namespace basist return false; } + if (pHeader->m_tex_format >= (uint32_t)basis_tex_format::cTotalFormats) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_image_info: invalid m_tex_format\n"); + return false; + } + const basis_slice_desc* pSlice_descs = reinterpret_cast(static_cast(pData) + pHeader->m_slice_desc_file_ofs); const basis_slice_desc& slice_desc = pSlice_descs[slice_index]; @@ -12169,6 +12248,12 @@ namespace basist const uint32_t block_width = basis_tex_format_get_block_width((basis_tex_format)((uint32_t)pHeader->m_tex_format)); const uint32_t block_height = basis_tex_format_get_block_height((basis_tex_format)((uint32_t)pHeader->m_tex_format)); + if (!check_slice_desc(slice_desc, block_width, block_height)) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_image_level_info: invalid slice_desc\n"); + return false; + } + image_info.m_iframe_flag = (slice_desc.m_flags & cSliceDescFlagsFrameIsIFrame) != 0; image_info.m_width = slice_desc.m_num_blocks_x * block_width; image_info.m_height = slice_desc.m_num_blocks_y * block_height; @@ -12226,6 +12311,11 @@ namespace basist file_info.m_tables_size = pHeader->m_tables_file_size; file_info.m_tex_format = static_cast(static_cast(pHeader->m_tex_format)); + if (file_info.m_tex_format >= basis_tex_format::cTotalFormats) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: invalid m_tex_format\n"); + return false; + } file_info.m_etc1s = (pHeader->m_tex_format == (int)basis_tex_format::cETC1S); @@ -12241,7 +12331,7 @@ namespace basist file_info.m_tex_type = static_cast(static_cast(pHeader->m_tex_type)); - if (file_info.m_tex_type > cBASISTexTypeTotal) + if (file_info.m_tex_type >= cBASISTexTypeTotal) { BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: invalid texture type, file is corrupted\n"); return false; @@ -12267,6 +12357,12 @@ namespace basist basisu_slice_info& slice_info = file_info.m_slice_info[i]; + if (!check_slice_desc(pSlice_descs[i], block_width, block_height)) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: invalid slice desc\n"); + return false; + } + slice_info.m_orig_width = pSlice_descs[i].m_orig_width; slice_info.m_orig_height = pSlice_descs[i].m_orig_height; slice_info.m_width = pSlice_descs[i].m_num_blocks_x * block_width; @@ -12458,6 +12554,12 @@ namespace basist const basis_slice_desc& slice_desc = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs)[slice_index]; + if (!check_slice_desc(pHeader, slice_desc)) + { + BASISU_DEVEL_ERROR("basisu_transcoder::transcode_slice: invalid basis_slice_desc\n"); + return false; + } + const uint32_t dst_block_width = get_block_width(fmt), dst_block_height = get_block_height(fmt); if (basis_block_format_is_uncompressed(fmt)) @@ -12732,7 +12834,7 @@ namespace basist const uint8_t* pDataU8 = static_cast(pData); const basis_slice_desc* pSlice_descs = reinterpret_cast(pDataU8 + pHeader->m_slice_desc_file_ofs); - + const bool basis_file_has_alpha_slices = (pHeader->m_flags & cBASISHeaderFlagHasAlphaSlices) != 0; int slice_index = find_first_slice_index(pData, data_size, image_index, level_index); @@ -12743,6 +12845,12 @@ namespace basist return false; } + if (!check_slice_desc(pHeader, pSlice_descs[slice_index])) + { + BASISU_DEVEL_ERROR("basisu_transcoder::transcode_image_level: invalid basis_slice_desc\n"); + return false; + } + if ((fmt == transcoder_texture_format::cTFPVRTC1_4_RGBA) && (!basis_file_has_alpha_slices)) { // Switch to PVRTC1 RGB if the input doesn't have alpha. diff --git a/transcoder/basisu_transcoder.h b/transcoder/basisu_transcoder.h index df0e9cd..e665b7a 100644 --- a/transcoder/basisu_transcoder.h +++ b/transcoder/basisu_transcoder.h @@ -35,6 +35,8 @@ namespace basist { + const uint32_t BASISU_MAX_SUPPORTED_TEXTURE_DIMENSION = 16384; + // High-level composite texture formats supported by the transcoder. // Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats. // Notes: