diff --git a/basisu_tool.cpp b/basisu_tool.cpp index 4895f67..741e91d 100644 --- a/basisu_tool.cpp +++ b/basisu_tool.cpp @@ -54,7 +54,7 @@ static void print_usage() "Important: By default, the compressor assumes the input is in the sRGB colorspace (like photos/albedo textures).\n" "If the input is NOT sRGB (like a normal map), be sure to specify -linear for less artifacts.\n" "\n" - "Filenames prefixed with a @ symbol are read as filename listing files.\n" + "Filenames prefixed with a @ symbol are read as filename listing files, specifying which filenames to actually process.\n" "\n" "Options:\n" " -file filename.png: Input image filename, multiple images are OK, use -file X for each input filename (prefixing input filenames with -file is optional)\n" @@ -74,6 +74,7 @@ static void print_usage() " (2d=arbitrary 2D images, 2darray=2D array, 3D=volume texture slices, video=video frames, cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's dimensions and # of mipmap levels must be the same.)\n" " -framerate X: Set framerate in header to X/frames sec\n" " -individual: Process input images individually and output multiple .basis files (not as a texture array)\n" + " -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents before transcoding\n" "\n" "More options:\n" " -max_endpoints X: Manually set the max number of color endpoint clusters from 1-16128, use instead of -q\n" @@ -207,7 +208,8 @@ public: m_multifile_first(0), m_multifile_num(0), m_individual(false), - m_no_ktx(false) + m_no_ktx(false), + m_fuzz_testing(false) { } @@ -443,6 +445,8 @@ public: } else if (strcasecmp(pArg, "-individual") == 0) m_individual = true; + else if (strcasecmp(pArg, "-fuzz_testing") == 0) + m_fuzz_testing = true; else if (strcasecmp(pArg, "-csv_file") == 0) { REMAINING_ARGS_CHECK(1); @@ -531,6 +535,7 @@ public: bool m_individual; bool m_no_ktx; + bool m_fuzz_testing; }; static bool expand_multifile(command_line_params &opts) @@ -790,22 +795,27 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl basist::basisu_transcoder dec(&sel_codebook); - // Validate the file - note this isn't necessary for transcoding - if (!dec.validate_file_checksums(&basis_data[0], (uint32_t)basis_data.size(), true)) + if (!opts.m_fuzz_testing) { - error_printf("File version is unsupported, or file fail CRC checks!\n"); - return false; + // Skip the full validation, which CRC16's the entire file. + + // Validate the file - note this isn't necessary for transcoding + if (!dec.validate_file_checksums(&basis_data[0], (uint32_t)basis_data.size(), true)) + { + error_printf("File version is unsupported, or file fail CRC checks!\n"); + return false; + } } printf("File version and CRC checks succeeded\n"); - + basist::basisu_file_info fileinfo; if (!dec.get_file_info(&basis_data[0], (uint32_t)basis_data.size(), fileinfo)) { error_printf("Failed retrieving Basis file information!\n"); return false; } - + assert(fileinfo.m_total_images == fileinfo.m_image_mipmap_levels.size()); assert(fileinfo.m_total_images == dec.get_total_images(&basis_data[0], (uint32_t)basis_data.size())); diff --git a/transcoder/basisu_transcoder.cpp b/transcoder/basisu_transcoder.cpp index fd80dd5..7349310 100644 --- a/transcoder/basisu_transcoder.cpp +++ b/transcoder/basisu_transcoder.cpp @@ -3336,13 +3336,19 @@ namespace basist BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2a\n"); return false; } - + if (!sym_codec.read_huffman_table(inten_delta_model)) { BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n"); return false; } + if (!color5_delta_model0.is_valid() || !color5_delta_model1.is_valid() || !color5_delta_model2.is_valid() || !inten_delta_model.is_valid()) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 2b\n"); + return false; + } + const bool endpoints_are_grayscale = sym_codec.get_bits(1) != 0; m_endpoints.resize(num_endpoints); @@ -3408,6 +3414,11 @@ namespace basist BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6\n"); return false; } + if (!mod_model.is_valid()) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 6a\n"); + return false; + } } for (uint32_t i = 0; i < num_selectors; i++) @@ -3445,6 +3456,11 @@ namespace basist BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7\n"); return false; } + if (!uses_global_cb_bitflags_model.is_valid()) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 7a\n"); + return false; + } basist::huffman_decoding_table global_mod_indices_model; if (mod_bits) @@ -3454,6 +3470,11 @@ namespace basist BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8\n"); return false; } + if (!global_mod_indices_model.is_valid()) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 8a\n"); + return false; + } } uint32_t cur_uses_global_cb_bitflags = 0; @@ -3519,7 +3540,16 @@ namespace basist else { if (!sym_codec.read_huffman_table(delta_selector_pal_model)) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10\n"); return false; + } + + if ((num_selectors > 1) && (!delta_selector_pal_model.is_valid())) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_palettes: fail 10a\n"); + return false; + } uint8_t prev_bytes[4] = { 0, 0, 0, 0 }; @@ -3574,10 +3604,22 @@ namespace basist BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1\n"); return false; } + + if (m_endpoint_pred_model.get_code_sizes().size() == 0) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1a\n"); + return false; + } if (!sym_codec.read_huffman_table(m_delta_endpoint_model)) { - BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 1\n"); + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2\n"); + return false; + } + + if (m_delta_endpoint_model.get_code_sizes().size() == 0) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 2a\n"); return false; } @@ -3587,12 +3629,24 @@ namespace basist return false; } + if (m_selector_model.get_code_sizes().size() == 0) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 3a\n"); + return false; + } + if (!sym_codec.read_huffman_table(m_selector_history_buf_rle_model)) { BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4\n"); return false; } + if (m_selector_history_buf_rle_model.get_code_sizes().size() == 0) + { + BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::decode_tables: fail 4a\n"); + return false; + } + m_selector_history_buf_size = sym_codec.get_bits(13); sym_codec.stop(); @@ -3791,6 +3845,16 @@ namespace basist assert(m_selector_history_buf_size > 0); int history_buf_index = selector_sym - (int)m_selectors.size(); + + if (history_buf_index >= (int)selector_history_buf.size()) + { + // The file is corrupted or we've got a bug. + //console::error("rdo_etc1_lowlevel_decoder::transcode_slice: selector RLE value is too large!"); + if (pPVRTC_work_mem) + free(pPVRTC_work_mem); + return false; + } + selector_index = selector_history_buf[history_buf_index]; if (history_buf_index != 0) @@ -4310,6 +4374,12 @@ namespace basist slice_info.m_unpacked_slice_crc16 = pSlice_descs[i].m_slice_data_crc16; slice_info.m_alpha_flag = (pSlice_descs[i].m_flags & cSliceDescFlagsIsAlphaData) != 0; + if (pSlice_descs[i].m_image_index >= pHeader->m_total_images) + { + BASISU_DEVEL_ERROR("basisu_transcoder::get_file_info: slice desc's image index is invalid\n"); + return false; + } + file_info.m_image_mipmap_levels[pSlice_descs[i].m_image_index] = basisu::maximum(file_info.m_image_mipmap_levels[pSlice_descs[i].m_image_index], pSlice_descs[i].m_level_index + 1); } diff --git a/transcoder/basisu_transcoder_internal.h b/transcoder/basisu_transcoder_internal.h index b864b09..186ad48 100644 --- a/transcoder/basisu_transcoder_internal.h +++ b/transcoder/basisu_transcoder_internal.h @@ -162,6 +162,8 @@ namespace basist const basisu::uint8_vec &get_code_sizes() const { return m_code_sizes; } + bool is_valid() const { return m_code_sizes.size() > 0; } + private: basisu::uint8_vec m_code_sizes; basisu::int_vec m_lookup;