From c2dae213821cb9f56a6ff3f65cc4f1bd20494366 Mon Sep 17 00:00:00 2001 From: bitsawer Date: Mon, 5 Jun 2023 12:15:30 +0300 Subject: [PATCH] tinyexr: Sync with upstream 1.0.4 (cherry picked from commit ca55c455adb03682a83155e48da2a60228f0aac0) --- thirdparty/README.md | 2 +- thirdparty/tinyexr/tinyexr.h | 406 +++++++++++++++++++++++++---------- 2 files changed, 298 insertions(+), 110 deletions(-) diff --git a/thirdparty/README.md b/thirdparty/README.md index 57cd75b9a88..89b78f81e78 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -538,7 +538,7 @@ comments and a patch is provided in the squish/ folder. ## tinyexr - Upstream: https://github.com/syoyo/tinyexr -- Version: 1.0.2 (02310c77e5156c36fedf6cf810c4071e3f83906f, 2023) +- Version: 1.0.4 (7c92b8cd86a378ba5cb7b6d39a336457728dfb82, 2023) - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h index 7482853bcb6..3613aaa874c 100644 --- a/thirdparty/tinyexr/tinyexr.h +++ b/thirdparty/tinyexr/tinyexr.h @@ -619,7 +619,6 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #endif #include -#include #include #include #include @@ -684,6 +683,27 @@ extern "C" unsigned char *stbi_zlib_compress(unsigned char *data, int data_len, #endif +// cond: conditional expression +// msg: std::string +// err: std::string* +#define TINYEXR_CHECK_AND_RETURN_MSG(cond, msg, err) do { \ + if (!(cond)) { \ + if (!err) { \ + std::ostringstream ss_e; \ + ss_e << __func__ << "():" << __LINE__ << msg << "\n"; \ + (*err) += ss_e.str(); \ + } \ + return false;\ + } \ + } while(0) + +// no error message. +#define TINYEXR_CHECK_AND_RETURN_C(cond, retcode) do { \ + if (!(cond)) { \ + return retcode; \ + } \ + } while(0) + namespace tinyexr { #if __cplusplus > 199711L @@ -1558,7 +1578,7 @@ static int rleUncompress(int inLength, int maxLength, const signed char in[], // End of RLE code from OpenEXR ----------------------------------- -static void CompressRle(unsigned char *dst, +static bool CompressRle(unsigned char *dst, tinyexr::tinyexr_uint64 &compressedSize, const unsigned char *src, unsigned long src_size) { std::vector tmpBuf(src_size); @@ -1613,7 +1633,7 @@ static void CompressRle(unsigned char *dst, int outSize = rleCompress(static_cast(src_size), reinterpret_cast(&tmpBuf.at(0)), reinterpret_cast(dst)); - assert(outSize > 0); + TINYEXR_CHECK_AND_RETURN_C(outSize > 0, false); compressedSize = static_cast(outSize); @@ -1623,6 +1643,8 @@ static void CompressRle(unsigned char *dst, compressedSize = src_size; memcpy(dst, src, src_size); } + + return true; } static bool DecompressRle(unsigned char *dst, @@ -2162,7 +2184,7 @@ struct FHeapCompare { bool operator()(long long *a, long long *b) { return *a > *b; } }; -static void hufBuildEncTable( +static bool hufBuildEncTable( long long *frq, // io: input frequencies [HUF_ENCSIZE], output table int *im, // o: min frq index int *iM) // o: max frq index @@ -2290,7 +2312,7 @@ static void hufBuildEncTable( for (int j = m;; j = hlink[j]) { scode[j]++; - assert(scode[j] <= 58); + TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false); if (hlink[j] == j) { // @@ -2309,7 +2331,7 @@ static void hufBuildEncTable( for (int j = mm;; j = hlink[j]) { scode[j]++; - assert(scode[j] <= 58); + TINYEXR_CHECK_AND_RETURN_C(scode[j] <= 58, false); if (hlink[j] == j) break; } @@ -2323,6 +2345,8 @@ static void hufBuildEncTable( hufCanonicalCodeTable(scode.data()); memcpy(frq, scode.data(), sizeof(long long) * HUF_ENCSIZE); + + return true; } // @@ -3034,7 +3058,6 @@ static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize, #if !TINYEXR_LITTLE_ENDIAN // @todo { PIZ compression on BigEndian architecture. } - assert(0); return false; #endif @@ -3160,7 +3183,6 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, #if !TINYEXR_LITTLE_ENDIAN // @todo { PIZ compression on BigEndian architecture. } - assert(0); return false; #endif @@ -3200,7 +3222,13 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, ptr += maxNonZero - minNonZero + 1; readLen += maxNonZero - minNonZero + 1; } else { - return false; + // Issue 194 + if ((minNonZero == (BITMAP_SIZE - 1)) && (maxNonZero == 0)) { + // OK. all pixels are zero. And no need to read `bitmap` data. + } else { + // invalid minNonZero/maxNonZero combination. + return false; + } } std::vector lut(USHORT_RANGE); @@ -3211,12 +3239,12 @@ static bool DecompressPiz(unsigned char *outPtr, const unsigned char *inPtr, // Huffman decoding // - int length; - if ((readLen + 4) > inLen) { return false; } + int length=0; + // length = *(reinterpret_cast(ptr)); tinyexr::cpy4(&length, reinterpret_cast(ptr)); ptr += sizeof(int); @@ -3396,8 +3424,8 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, zfp_stream *zfp = NULL; zfp_field *field = NULL; - assert((dst_width % 4) == 0); - assert((dst_num_lines % 4) == 0); + TINYEXR_CHECK_AND_RETURN_C((dst_width % 4) == 0, false); + TINYEXR_CHECK_AND_RETURN_C((dst_num_lines % 4) == 0, false); if ((size_t(dst_width) & 3U) || (size_t(dst_num_lines) & 3U)) { return false; @@ -3418,7 +3446,7 @@ static bool DecompressZfp(float *dst, int dst_width, int dst_num_lines, } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { zfp_stream_set_accuracy(zfp, param.tolerance); } else { - assert(0); + return false; } size_t buf_size = zfp_stream_maximum_size(zfp, field); @@ -3462,8 +3490,8 @@ static bool CompressZfp(std::vector *outBuf, zfp_stream *zfp = NULL; zfp_field *field = NULL; - assert((width % 4) == 0); - assert((num_lines % 4) == 0); + TINYEXR_CHECK_AND_RETURN_C((width % 4) == 0, false); + TINYEXR_CHECK_AND_RETURN_C((num_lines % 4) == 0, false); if ((size_t(width) & 3U) || (size_t(num_lines) & 3U)) { return false; @@ -3483,7 +3511,7 @@ static bool CompressZfp(std::vector *outBuf, } else if (param.type == TINYEXR_ZFP_COMPRESSIONTYPE_ACCURACY) { zfp_stream_set_accuracy(zfp, param.tolerance); } else { - assert(0); + return false; } size_t buf_size = zfp_stream_maximum_size(zfp, field); @@ -3620,7 +3648,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const unsigned int *line_ptr = reinterpret_cast( @@ -3649,7 +3677,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const float *line_ptr = reinterpret_cast(&outBuf.at( v * pixel_data_size * static_cast(width) + @@ -3676,11 +3704,10 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else { - assert(0); + return false; } } #else - assert(0 && "PIZ is disabled in this build"); return false; #endif @@ -3692,7 +3719,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, pixel_data_size); unsigned long dstLen = static_cast(outBuf.size()); - assert(dstLen > 0); + TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false); if (!tinyexr::DecompressZip( reinterpret_cast(&outBuf.at(0)), &dstLen, data_ptr, static_cast(data_len))) { @@ -3759,7 +3786,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const unsigned int *line_ptr = reinterpret_cast( @@ -3788,7 +3815,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const float *line_ptr = reinterpret_cast( &outBuf.at(v * pixel_data_size * static_cast(width) + @@ -3815,7 +3842,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else { - assert(0); return false; } } @@ -3893,7 +3919,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_UINT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_UINT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const unsigned int *line_ptr = reinterpret_cast( @@ -3922,7 +3948,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const float *line_ptr = reinterpret_cast( &outBuf.at(v * pixel_data_size * static_cast(width) + @@ -3949,7 +3975,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else { - assert(0); return false; } } @@ -3960,7 +3985,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, if (!tinyexr::FindZFPCompressionParam(&zfp_compression_param, attributes, int(num_attributes), &e)) { // This code path should not be reachable. - assert(0); return false; } @@ -3970,7 +3994,7 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, pixel_data_size); unsigned long dstLen = outBuf.size(); - assert(dstLen > 0); + TINYEXR_CHECK_AND_RETURN_C(dstLen > 0, false); tinyexr::DecompressZfp(reinterpret_cast(&outBuf.at(0)), width, num_lines, num_channels, data_ptr, static_cast(data_len), @@ -3987,9 +4011,9 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, // pixel sample data for channel n for scanline 1 // ... for (size_t c = 0; c < static_cast(num_channels); c++) { - assert(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT); + TINYEXR_CHECK_AND_RETURN_C(channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT, false); if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { - assert(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT); + TINYEXR_CHECK_AND_RETURN_C(requested_pixel_types[c] == TINYEXR_PIXELTYPE_FLOAT, false); for (size_t v = 0; v < static_cast(num_lines); v++) { const float *line_ptr = reinterpret_cast( &outBuf.at(v * pixel_data_size * static_cast(width) + @@ -4015,7 +4039,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, } } } else { - assert(0); return false; } } @@ -4023,7 +4046,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, (void)attributes; (void)num_attributes; (void)num_channels; - assert(0); return false; #endif } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { @@ -4084,7 +4106,6 @@ static bool DecodePixelData(/* out */ unsigned char **out_images, outLine[u] = f32.f; } } else { - assert(0); return false; } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { @@ -4245,7 +4266,9 @@ static unsigned char **AllocateImage(int num_channels, images[c] = reinterpret_cast( static_cast(malloc(sizeof(float) * data_len))); } else { - assert(0); + images[c] = NULL; // just in case. + valid = false; + break; } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { // pixel_data_size += sizeof(float); @@ -4400,7 +4423,6 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, return TINYEXR_ERROR_INVALID_DATA; } - assert(data.size() == 9); memcpy(&x_size, &data.at(0), sizeof(int)); memcpy(&y_size, &data.at(4), sizeof(int)); tile_mode = data[8]; @@ -4787,6 +4809,7 @@ struct OffsetData { int num_y_levels; }; +// -1 = error static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) { switch (tile_level_mode) { case TINYEXR_TILE_ONE_LEVEL: @@ -4799,13 +4822,15 @@ static int LevelIndex(int lx, int ly, int tile_level_mode, int num_x_levels) { return lx + ly * num_x_levels; default: - assert(false); + return -1; } return 0; } static int LevelSize(int toplevel_size, int level, int tile_rounding_mode) { - assert(level >= 0); + if (level < 0) { + return -1; + } int b = static_cast(1u << static_cast(level)); int level_size = toplevel_size / b; @@ -4826,9 +4851,13 @@ static int DecodeTiledLevel(EXRImage* exr_image, const EXRHeader* exr_header, int level_index = LevelIndex(exr_image->level_x, exr_image->level_y, exr_header->tile_level_mode, offset_data.num_x_levels); int num_y_tiles = int(offset_data.offsets[size_t(level_index)].size()); - assert(num_y_tiles); + if (num_y_tiles < 1) { + return TINYEXR_ERROR_INVALID_DATA; + } int num_x_tiles = int(offset_data.offsets[size_t(level_index)][0].size()); - assert(num_x_tiles); + if (num_x_tiles < 1) { + return TINYEXR_ERROR_INVALID_DATA; + } int num_tiles = num_x_tiles * num_y_tiles; int err_code = TINYEXR_SUCCESS; @@ -5026,10 +5055,24 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, return TINYEXR_ERROR_INVALID_DATA; } - int data_width = - exr_header->data_window.max_x - exr_header->data_window.min_x + 1; - int data_height = - exr_header->data_window.max_y - exr_header->data_window.min_y + 1; + tinyexr_int64 data_width = + static_cast(exr_header->data_window.max_x) - static_cast(exr_header->data_window.min_x) + static_cast(1); + tinyexr_int64 data_height = + static_cast(exr_header->data_window.max_y) - static_cast(exr_header->data_window.min_y) + static_cast(1); + + if (data_width <= 0) { + if (err) { + (*err) += "Invalid data window width.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + + if (data_height <= 0) { + if (err) { + (*err) += "Invalid data window height.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } // Do not allow too large data_width and data_height. header invalid? { @@ -5109,8 +5152,17 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, } level_image->width = LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level, exr_header->tile_rounding_mode); + if (level_image->width < 1) { + return TINYEXR_ERROR_INVALID_DATA; + } + level_image->height = LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level, exr_header->tile_rounding_mode); + + if (level_image->height < 1) { + return TINYEXR_ERROR_INVALID_DATA; + } + level_image->level_x = level; level_image->level_y = level; @@ -5136,8 +5188,16 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, level_image->width = LevelSize(exr_header->data_window.max_x - exr_header->data_window.min_x + 1, level_x, exr_header->tile_rounding_mode); + if (level_image->width < 1) { + return TINYEXR_ERROR_INVALID_DATA; + } + level_image->height = LevelSize(exr_header->data_window.max_y - exr_header->data_window.min_y + 1, level_y, exr_header->tile_rounding_mode); + if (level_image->height < 1) { + return TINYEXR_ERROR_INVALID_DATA; + } + level_image->level_x = level_x; level_image->level_y = level_y; @@ -5171,7 +5231,7 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, bool alloc_success = false; exr_image->images = tinyexr::AllocateImage( num_channels, exr_header->channels, exr_header->requested_pixel_types, - data_width, data_height, &alloc_success); + int(data_width), int(data_height), &alloc_success); if (!alloc_success) { if (err) { @@ -5271,7 +5331,7 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, exr_image->images, exr_header->requested_pixel_types, data_ptr, static_cast(data_len), exr_header->compression_type, exr_header->line_order, - data_width, data_height, data_width, y, line_no, + int(data_width), int(data_height), int(data_width), y, line_no, num_lines, static_cast(pixel_data_size), static_cast( exr_header->num_custom_attributes), @@ -5323,8 +5383,8 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, { exr_image->num_channels = num_channels; - exr_image->width = data_width; - exr_image->height = data_height; + exr_image->width = int(data_width); + exr_image->height = int(data_height); } return TINYEXR_SUCCESS; @@ -5333,8 +5393,12 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, static bool ReconstructLineOffsets( std::vector *offsets, size_t n, const unsigned char *head, const unsigned char *marker, const size_t size) { - assert(head < marker); - assert(offsets->size() == n); + if (head >= marker) { + return false; + } + if (offsets->size() != n) { + return false; + } for (size_t i = 0; i < n; i++) { size_t offset = static_cast(marker - head); @@ -5430,7 +5494,7 @@ static int CalculateNumXLevels(const EXRHeader* exr_header) { default: - assert(false); + return -1; } return num; @@ -5468,25 +5532,29 @@ static int CalculateNumYLevels(const EXRHeader* exr_header) { default: - assert(false); + return -1; } return num; } -static void CalculateNumTiles(std::vector& numTiles, +static bool CalculateNumTiles(std::vector& numTiles, int toplevel_size, int size, int tile_rounding_mode) { for (unsigned i = 0; i < numTiles.size(); i++) { int l = LevelSize(toplevel_size, int(i), tile_rounding_mode); - assert(l <= std::numeric_limits::max() - size + 1); + if (l < 0) { + return false; + } + TINYEXR_CHECK_AND_RETURN_C(l <= std::numeric_limits::max() - size + 1, false); numTiles[i] = (l + size - 1) / size; } + return true; } -static void PrecalculateTileInfo(std::vector& num_x_tiles, +static bool PrecalculateTileInfo(std::vector& num_x_tiles, std::vector& num_y_tiles, const EXRHeader* exr_header) { int min_x = exr_header->data_window.min_x; @@ -5495,20 +5563,35 @@ static void PrecalculateTileInfo(std::vector& num_x_tiles, int max_y = exr_header->data_window.max_y; int num_x_levels = CalculateNumXLevels(exr_header); + + if (num_x_levels < 0) { + return false; + } + int num_y_levels = CalculateNumYLevels(exr_header); + if (num_y_levels < 0) { + return false; + } + num_x_tiles.resize(size_t(num_x_levels)); num_y_tiles.resize(size_t(num_y_levels)); - CalculateNumTiles(num_x_tiles, + if (!CalculateNumTiles(num_x_tiles, max_x - min_x + 1, exr_header->tile_size_x, - exr_header->tile_rounding_mode); + exr_header->tile_rounding_mode)) { + return false; + } - CalculateNumTiles(num_y_tiles, + if (!CalculateNumTiles(num_y_tiles, max_y - min_y + 1, exr_header->tile_size_y, - exr_header->tile_rounding_mode); + exr_header->tile_rounding_mode)) { + return false; + } + + return true; } static void InitSingleResolutionOffsets(OffsetData& offset_data, size_t num_blocks) { @@ -5520,6 +5603,7 @@ static void InitSingleResolutionOffsets(OffsetData& offset_data, size_t num_bloc } // Return sum of tile blocks. +// 0 = error static int InitTileOffsets(OffsetData& offset_data, const EXRHeader* exr_header, const std::vector& num_x_tiles, @@ -5530,7 +5614,7 @@ static int InitTileOffsets(OffsetData& offset_data, switch (exr_header->tile_level_mode) { case TINYEXR_TILE_ONE_LEVEL: case TINYEXR_TILE_MIPMAP_LEVELS: - assert(offset_data.num_x_levels == offset_data.num_y_levels); + TINYEXR_CHECK_AND_RETURN_C(offset_data.num_x_levels == offset_data.num_y_levels, 0); offset_data.offsets.resize(size_t(offset_data.num_x_levels)); for (unsigned int l = 0; l < offset_data.offsets.size(); ++l) { @@ -5561,7 +5645,7 @@ static int InitTileOffsets(OffsetData& offset_data, break; default: - assert(false); + return 0; } return num_tile_blocks; } @@ -5629,7 +5713,7 @@ static bool isValidTile(const EXRHeader* exr_header, return false; } -static void ReconstructTileOffsets(OffsetData& offset_data, +static bool ReconstructTileOffsets(OffsetData& offset_data, const EXRHeader* exr_header, const unsigned char* head, const unsigned char* marker, const size_t /*size*/, bool isMultiPartFile, @@ -5689,14 +5773,19 @@ static void ReconstructTileOffsets(OffsetData& offset_data, } if (!isValidTile(exr_header, offset_data, - tileX, tileY, levelX, levelY)) - return; + tileX, tileY, levelX, levelY)) { + return false; + } int level_idx = LevelIndex(levelX, levelY, exr_header->tile_level_mode, numXLevels); + if (level_idx < 0) { + return false; + } offset_data.offsets[size_t(level_idx)][size_t(tileY)][size_t(tileX)] = tileOffset; } } } + return true; } // marker output is also @@ -5754,8 +5843,12 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, tinyexr::SetErrorMessage("Invalid data width value", err); return TINYEXR_ERROR_INVALID_DATA; } - int data_width = - exr_header->data_window.max_x - exr_header->data_window.min_x + 1; + tinyexr_int64 data_width = + static_cast(exr_header->data_window.max_x) - static_cast(exr_header->data_window.min_x) + static_cast(1); + if (data_width <= 0) { + tinyexr::SetErrorMessage("Invalid data window width value", err); + return TINYEXR_ERROR_INVALID_DATA; + } if (exr_header->data_window.max_y < exr_header->data_window.min_y || exr_header->data_window.max_y - exr_header->data_window.min_y == @@ -5763,8 +5856,13 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, tinyexr::SetErrorMessage("Invalid data height value", err); return TINYEXR_ERROR_INVALID_DATA; } - int data_height = - exr_header->data_window.max_y - exr_header->data_window.min_y + 1; + tinyexr_int64 data_height = + static_cast(exr_header->data_window.max_y) - static_cast(exr_header->data_window.min_y) + static_cast(1); + + if (data_height <= 0) { + tinyexr::SetErrorMessage("Invalid data window height value", err); + return TINYEXR_ERROR_INVALID_DATA; + } // Do not allow too large data_width and data_height. header invalid? { @@ -5797,7 +5895,10 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, if (exr_header->tiled) { { std::vector num_x_tiles, num_y_tiles; - PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header); + if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_header)) { + tinyexr::SetErrorMessage("Failed to precalculate tile info.", err); + return TINYEXR_ERROR_INVALID_DATA; + } num_blocks = size_t(InitTileOffsets(offset_data, exr_header, num_x_tiles, num_y_tiles)); if (exr_header->chunk_count > 0) { if (exr_header->chunk_count != static_cast(num_blocks)) { @@ -5810,9 +5911,13 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, int ret = ReadOffsets(offset_data, head, marker, size, err); if (ret != TINYEXR_SUCCESS) return ret; if (IsAnyOffsetsAreInvalid(offset_data)) { - ReconstructTileOffsets(offset_data, exr_header, + if (!ReconstructTileOffsets(offset_data, exr_header, head, marker, size, - exr_header->multipart, exr_header->non_image); + exr_header->multipart, exr_header->non_image)) { + + tinyexr::SetErrorMessage("Invalid Tile Offsets data.", err); + return TINYEXR_ERROR_INVALID_DATA; + } } } else if (exr_header->chunk_count > 0) { // Use `chunkCount` attribute. @@ -6638,6 +6743,7 @@ struct MemoryMappedFile { data = reinterpret_cast( mmap(0, size, PROT_READ, MAP_SHARED, posix_descriptor, 0)); if (data == MAP_FAILED) { + data = nullptr; return; } #else @@ -6662,20 +6768,26 @@ struct MemoryMappedFile { size = static_cast(ftell_result); if (fseek(fp, 0, SEEK_SET) != 0) { fclose(fp); + size = 0; return; } data = reinterpret_cast(malloc(size)); if (!data) { + size = 0; fclose(fp); return; } size_t read_bytes = fread(data, 1, size, fp); - assert(read_bytes == size); + if (read_bytes != size) { + // TODO: Try to read data until reading `size` bytes. + fclose(fp); + size = 0; + data = nullptr; + return; + } fclose(fp); - (void)read_bytes; #endif - assert(valid()); } // MemoryMappedFile's destructor closes all its handles. @@ -6966,9 +7078,14 @@ static bool EncodePixelData(/* out */ std::vector& out_data, tinyexr::tinyexr_uint64 outSize = block.size(); - tinyexr::CompressRle(&block.at(0), outSize, + if (!tinyexr::CompressRle(&block.at(0), outSize, reinterpret_cast(&buf.at(0)), - static_cast(buf.size())); + static_cast(buf.size()))) { + if (err) { + (*err) += "RLE compresssion failed.\n"; + } + return false; + } // 4 byte: scan line // 4 byte: data size @@ -6985,9 +7102,14 @@ static bool EncodePixelData(/* out */ std::vector& out_data, std::vector block(bufLen); unsigned int outSize = static_cast(block.size()); - CompressPiz(&block.at(0), &outSize, + if (!CompressPiz(&block.at(0), &outSize, reinterpret_cast(&buf.at(0)), - buf.size(), channels, width, num_lines); + buf.size(), channels, width, num_lines)) { + if (err) { + (*err) += "PIZ compresssion failed.\n"; + } + return false; + } // 4 byte: scan line // 4 byte: data size @@ -7041,14 +7163,19 @@ static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_he const void* compression_param, // must be set if zfp compression is enabled std::string* err) { int num_tiles = num_x_tiles * num_y_tiles; - assert(num_tiles == level_image->num_tiles); + if (num_tiles != level_image->num_tiles) { + if (err) { + (*err) += "Invalid number of tiles in argument.\n"; + } + return TINYEXR_ERROR_INVALID_ARGUMENT; + } if ((exr_header->tile_size_x > level_image->width || exr_header->tile_size_y > level_image->height) && level_image->level_x == 0 && level_image->level_y == 0) { if (err) { (*err) += "Failed to encode tile data.\n"; - } - return TINYEXR_ERROR_INVALID_DATA; + } + return TINYEXR_ERROR_INVALID_DATA; } @@ -7111,7 +7238,11 @@ static int EncodeTiledLevel(const EXRImage* level_image, const EXRHeader* exr_he invalid_data = true; continue; } - assert(data_list[data_idx].size() > data_header_size); + if (data_list[data_idx].size() <= data_header_size) { + invalid_data = true; + continue; + } + int data_len = static_cast(data_list[data_idx].size() - data_header_size); //tileX, tileY, levelX, levelY // pixel_data_size(int) memcpy(&data_list[data_idx][0], &x_tile, sizeof(int)); @@ -7191,7 +7322,10 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header, pixel_data_size += sizeof(unsigned int); channel_offset += sizeof(unsigned int); } else { - assert(0); + if (err) { + (*err) += "Invalid requested_pixel_type.\n"; + } + return TINYEXR_ERROR_INVALID_DATA; } } } @@ -7236,6 +7370,13 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header, int level_index_from_image = LevelIndex(level_image->level_x, level_image->level_y, exr_header->tile_level_mode, offset_data.num_x_levels); + if (level_index_from_image < 0) { + if (err) { + (*err) += "Invalid tile level mode\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + if (level_index_from_image != level_index) { if (err) { (*err) += "Incorrect level ordering in tiled image\n"; @@ -7243,9 +7384,20 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header, return TINYEXR_ERROR_INVALID_DATA; } int num_y_tiles = int(offset_data.offsets[level_index].size()); - assert(num_y_tiles); + if (num_y_tiles <= 0) { + if (err) { + (*err) += "Invalid Y tile size\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } + int num_x_tiles = int(offset_data.offsets[level_index][0].size()); - assert(num_x_tiles); + if (num_x_tiles <= 0) { + if (err) { + (*err) += "Invalid X tile size\n"; + } + return TINYEXR_ERROR_INVALID_DATA; + } std::string e; int ret = EncodeTiledLevel(level_image, @@ -7276,7 +7428,7 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header, } level_image = level_image->next_level; } - assert(static_cast(block_idx) == num_blocks); + TINYEXR_CHECK_AND_RETURN_C(static_cast(block_idx) == num_blocks, TINYEXR_ERROR_INVALID_DATA); total_size = offset; } else { // scanlines std::vector& offsets = offset_data.offsets[0][0]; @@ -7329,7 +7481,10 @@ static int EncodeChunk(const EXRImage* exr_image, const EXRHeader* exr_header, invalid_data = true; continue; // "break" cannot be used with OpenMP } - assert(data_list[i].size() > data_header_size); + if (data_list[i].size() <= data_header_size) { + invalid_data = true; + continue; // "break" cannot be used with OpenMP + } int data_len = static_cast(data_list[i].size() - data_header_size); memcpy(&data_list[i][0], &start_y, sizeof(int)); memcpy(&data_list[i][4], &data_len, sizeof(int)); @@ -7455,9 +7610,20 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images, } else { { std::vector num_x_tiles, num_y_tiles; - PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]); - chunk_count[i] = - InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles); + if (!PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) { + SetErrorMessage("Failed to precalculate Tile info", + err); + return TINYEXR_ERROR_INVALID_DATA; + } + int ntiles = InitTileOffsets(offset_data[i], exr_headers[i], num_x_tiles, num_y_tiles); + if (ntiles > 0) { + chunk_count[i] = ntiles; + } else { + SetErrorMessage("Failed to compute Tile offsets", + err); + return TINYEXR_ERROR_INVALID_DATA; + + } total_chunk_count += chunk_count[i]; } } @@ -7657,7 +7823,7 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images, // Allocating required memory if (total_size == 0) { // something went wrong tinyexr::SetErrorMessage("Output memory size is zero", err); - return 0; + return TINYEXR_ERROR_INVALID_DATA; } (*memory_out) = static_cast(malloc(size_t(total_size))); @@ -7676,7 +7842,11 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images, for (size_t j = 0; j < offset_data[i].offsets[level_index].size(); ++j) { size_t num_bytes = sizeof(tinyexr_uint64) * offset_data[i].offsets[level_index][j].size(); sum += num_bytes; - assert(sum <= total_size); + if (sum > total_size) { + tinyexr::SetErrorMessage("Invalid offset bytes in Tiled Part image.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + memcpy(memory_ptr, reinterpret_cast(&offset_data[i].offsets[level_index][j][0]), num_bytes); @@ -7687,7 +7857,10 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images, } else { size_t num_bytes = sizeof(tinyexr::tinyexr_uint64) * static_cast(chunk_count[i]); sum += num_bytes; - assert(sum <= total_size); + if (sum > total_size) { + tinyexr::SetErrorMessage("Invalid offset bytes in Part image.", err); + return TINYEXR_ERROR_INVALID_DATA; + } std::vector& offsets = offset_data[i].offsets[0][0]; memcpy(memory_ptr, reinterpret_cast(&offsets[0]), num_bytes); memory_ptr += num_bytes; @@ -7699,19 +7872,30 @@ static size_t SaveEXRNPartImageToMemory(const EXRImage* exr_images, for (size_t j = 0; j < static_cast(chunk_count[i]); ++j) { if (num_parts > 1) { sum += 4; - assert(sum <= total_size); + if (sum > total_size) { + tinyexr::SetErrorMessage("Buffer overrun in reading Part image chunk data.", err); + return TINYEXR_ERROR_INVALID_DATA; + } unsigned int part_number = i; swap4(&part_number); memcpy(memory_ptr, &part_number, 4); memory_ptr += 4; } sum += data_lists[i][j].size(); - assert(sum <= total_size); + if (sum > total_size) { + tinyexr::SetErrorMessage("Buffer overrun in reading Part image chunk data.", err); + return TINYEXR_ERROR_INVALID_DATA; + } memcpy(memory_ptr, &data_lists[i][j][0], data_lists[i][j].size()); memory_ptr += data_lists[i][j].size(); } } - assert(sum == total_size); + + if (sum != total_size) { + tinyexr::SetErrorMessage("Corrupted Part image chunk data.", err); + return TINYEXR_ERROR_INVALID_DATA; + } + return size_t(total_size); // OK } @@ -8004,11 +8188,11 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { } } - assert(dx >= 0); - assert(dy >= 0); - assert(dw >= 0); - assert(dh >= 0); - assert(num_channels >= 1); + TINYEXR_CHECK_AND_RETURN_C(dx >= 0, TINYEXR_ERROR_INVALID_DATA); + TINYEXR_CHECK_AND_RETURN_C(dy >= 0, TINYEXR_ERROR_INVALID_DATA); + TINYEXR_CHECK_AND_RETURN_C(dw >= 0, TINYEXR_ERROR_INVALID_DATA); + TINYEXR_CHECK_AND_RETURN_C(dh >= 0, TINYEXR_ERROR_INVALID_DATA); + TINYEXR_CHECK_AND_RETURN_C(num_channels >= 1, TINYEXR_ERROR_INVALID_DATA); int data_width = dw - dx + 1; int data_height = dh - dy + 1; @@ -8106,7 +8290,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { return false; } - assert(dstLen == pixelOffsetTable.size() * sizeof(int)); + TINYEXR_CHECK_AND_RETURN_C(dstLen == pixelOffsetTable.size() * sizeof(int), TINYEXR_ERROR_INVALID_DATA); for (size_t i = 0; i < static_cast(data_width); i++) { deep_image->offset_table[y][i] = pixelOffsetTable[i]; } @@ -8125,7 +8309,7 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { static_cast(packedSampleDataSize))) { return false; } - assert(dstLen == static_cast(unpackedSampleDataSize)); + TINYEXR_CHECK_AND_RETURN_C(dstLen == static_cast(unpackedSampleDataSize), TINYEXR_ERROR_INVALID_DATA); } } @@ -8144,16 +8328,17 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { TINYEXR_PIXELTYPE_FLOAT) { // float channel_offset += 4; } else { - assert(0); + tinyexr::SetErrorMessage("Invalid pixel_type in chnnels.", err); + return TINYEXR_ERROR_INVALID_DATA; } } sampleSize = channel_offset; } - assert(sampleSize >= 2); + TINYEXR_CHECK_AND_RETURN_C(sampleSize >= 2, TINYEXR_ERROR_INVALID_DATA); - assert(static_cast( + TINYEXR_CHECK_AND_RETURN_C(static_cast( pixelOffsetTable[static_cast(data_width - 1)] * - sampleSize) == sample_data.size()); + sampleSize) == sample_data.size(), TINYEXR_ERROR_INVALID_DATA); int samples_per_line = static_cast(sample_data.size()) / sampleSize; // @@ -8656,7 +8841,10 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, } else { { std::vector num_x_tiles, num_y_tiles; - tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i]); + if (!tinyexr::PrecalculateTileInfo(num_x_tiles, num_y_tiles, exr_headers[i])) { + tinyexr::SetErrorMessage("Invalid tile info.", err); + return TINYEXR_ERROR_INVALID_DATA; + } int num_blocks = InitTileOffsets(offset_data, exr_headers[i], num_x_tiles, num_y_tiles); if (num_blocks != exr_headers[i]->chunk_count) { tinyexr::SetErrorMessage("Invalid offset table size.", err);