diff --git a/thirdparty/README.md b/thirdparty/README.md index ac1c0196da84..792e3b165071 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -431,7 +431,7 @@ comments and a patch is provided in the squish/ folder. ## tinyexr - Upstream: https://github.com/syoyo/tinyexr -- Version: 0.9.5+ (git 9f784ca - 24 October 2017) +- Version: git (e385dad, 2018) - License: BSD-3-Clause Files extracted from upstream source: diff --git a/thirdparty/tinyexr/tinyexr.h b/thirdparty/tinyexr/tinyexr.h index 606c19756a53..107c22ffb357 100644 --- a/thirdparty/tinyexr/tinyexr.h +++ b/thirdparty/tinyexr/tinyexr.h @@ -410,8 +410,8 @@ extern int LoadDeepEXR(DeepImage *out_image, const char *filename, // Returns negative value and may set error string in `err` when there's an // error extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, - const unsigned char *memory, size_t size, - const char **err); + const unsigned char *memory, size_t size, + const char **err); #ifdef __cplusplus } @@ -444,7 +444,8 @@ extern int LoadEXRFromMemory(float **out_rgba, int *width, int *height, #if TINYEXR_USE_MINIZ #else -// Issue #46. Please include your own zlib-compatible API header before including `tinyexr.h` +// Issue #46. Please include your own zlib-compatible API header before +// including `tinyexr.h` //#include "zlib.h" #endif @@ -488,6 +489,12 @@ namespace miniz { #if __has_warning("-Wcomma") #pragma clang diagnostic ignored "-Wcomma" #endif +#if __has_warning("-Wmacro-redefined") +#pragma clang diagnostic ignored "-Wmacro-redefined" +#endif +#if __has_warning("-Wcast-qual") +#pragma clang diagnostic ignored "-Wcast-qual" +#endif #endif /* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP @@ -6887,8 +6894,6 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, #ifdef _MSC_VER #pragma warning(pop) #endif - - } #else @@ -7079,11 +7084,18 @@ static FP16 float_to_half_full(FP32 f) { // #define IMF_B44_COMPRESSION 6 // #define IMF_B44A_COMPRESSION 7 -static const char *ReadString(std::string *s, const char *ptr) { +static const char *ReadString(std::string *s, const char *ptr, size_t len) { // Read untile NULL(\0). const char *p = ptr; const char *q = ptr; - while ((*q) != 0) q++; + while ((size_t(q - ptr) < len) && (*q) != 0) { + q++; + } + + if (size_t(q - ptr) >= len) { + (*s) = std::string(); + return NULL; + } (*s) = std::string(p, q); @@ -7120,6 +7132,10 @@ static bool ReadAttribute(std::string *name, std::string *type, memcpy(&data_len, marker, sizeof(uint32_t)); tinyexr::swap4(reinterpret_cast(&data_len)); + if (data_len == 0) { + return false; + } + marker += sizeof(uint32_t); size -= sizeof(uint32_t); @@ -7210,7 +7226,7 @@ typedef struct { } } HeaderInfo; -static void ReadChannelInfo(std::vector &channels, +static bool ReadChannelInfo(std::vector &channels, const std::vector &data) { const char *p = reinterpret_cast(&data.at(0)); @@ -7219,7 +7235,18 @@ static void ReadChannelInfo(std::vector &channels, break; } ChannelInfo info; - p = ReadString(&info.name, p); + + tinyexr_int64 data_len = static_cast(data.size()) - (p - reinterpret_cast(data.data())); + if (data_len < 0) { + return false; + } + + p = ReadString( + &info.name, p, size_t(data_len)); + if ((p == NULL) && (info.name.empty())) { + // Buffer overrun. Issue #51. + return false; + } memcpy(&info.pixel_type, p, sizeof(int)); p += 4; @@ -7236,6 +7263,8 @@ static void ReadChannelInfo(std::vector &channels, channels.push_back(info); } + + return true; } static void WriteChannelInfo(std::vector &data, @@ -7361,25 +7390,27 @@ static void CompressZip(unsigned char *dst, } } -static void DecompressZip(unsigned char *dst, +static bool DecompressZip(unsigned char *dst, unsigned long *uncompressed_size /* inout */, const unsigned char *src, unsigned long src_size) { if ((*uncompressed_size) == src_size) { // Data is not compressed(Issue 40). memcpy(dst, src, src_size); - return; + return true; } std::vector tmpBuf(*uncompressed_size); #if TINYEXR_USE_MINIZ int ret = miniz::mz_uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); - assert(ret == miniz::MZ_OK); - (void)ret; + if (miniz::MZ_OK != ret) { + return false; + } #else int ret = uncompress(&tmpBuf.at(0), uncompressed_size, src, src_size); - assert(ret == Z_OK); - (void)ret; + if (Z_OK != ret) { + return false; + } #endif // @@ -7419,6 +7450,8 @@ static void DecompressZip(unsigned char *dst, break; } } + + return true; } // RLE code from OpenEXR -------------------------------------- @@ -7443,7 +7476,6 @@ static void DecompressZip(unsigned char *dst, // conformant name: _strdup. #endif - const int MIN_RUN_LENGTH = 3; const int MAX_RUN_LENGTH = 127; @@ -7673,6 +7705,11 @@ static void DecompressRle(unsigned char *dst, #pragma clang diagnostic ignored "-Wsign-conversion" #pragma clang diagnostic ignored "-Wc++11-extensions" #pragma clang diagnostic ignored "-Wconversion" + +#if __has_warning("-Wcast-qual") +#pragma clang diagnostic ignored "-Wcast-qual" +#endif + #endif // @@ -8934,7 +8971,6 @@ static void applyLut(const unsigned short lut[USHORT_RANGE], #pragma warning(pop) #endif - static bool CompressPiz(unsigned char *outPtr, unsigned int *outSize, const unsigned char *inPtr, size_t inSize, const std::vector &channelInfo, @@ -9373,7 +9409,7 @@ bool CompressZfp(std::vector *outBuf, unsigned int *outSize, // ----------------------------------------------------------------- // -static void DecodePixelData(/* out */ unsigned char **out_images, +static bool DecodePixelData(/* out */ unsigned char **out_images, const int *requested_pixel_types, const unsigned char *data_ptr, size_t data_len, int compression_type, int line_order, int width, @@ -9509,6 +9545,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, } #else assert(0 && "PIZ is enabled in this build"); + return false; #endif } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZIPS || @@ -9520,9 +9557,11 @@ static void DecodePixelData(/* out */ unsigned char **out_images, unsigned long dstLen = static_cast(outBuf.size()); assert(dstLen > 0); - tinyexr::DecompressZip(reinterpret_cast(&outBuf.at(0)), + if (!tinyexr::DecompressZip(reinterpret_cast(&outBuf.at(0)), &dstLen, data_ptr, - static_cast(data_len)); + static_cast(data_len))) { + return false; + } // For ZIP_COMPRESSION: // pixel sample data for channel 0 for scanline 0 @@ -9633,6 +9672,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, } } else { assert(0); + return false; } } } else if (compression_type == TINYEXR_COMPRESSIONTYPE_RLE) { @@ -9756,6 +9796,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, } } else { assert(0); + return false; } } } else if (compression_type == TINYEXR_COMPRESSIONTYPE_ZFP) { @@ -9764,7 +9805,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, if (!FindZFPCompressionParam(&zfp_compression_param, attributes, num_attributes)) { assert(0); - return; + return false; } // Allocate original data size. @@ -9818,6 +9859,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, } } else { assert(0); + return false; } } #else @@ -9825,6 +9867,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, (void)num_attributes; (void)num_channels; assert(0); + return false; #endif } else if (compression_type == TINYEXR_COMPRESSIONTYPE_NONE) { for (size_t c = 0; c < num_channels; c++) { @@ -9873,6 +9916,7 @@ static void DecodePixelData(/* out */ unsigned char **out_images, } } else { assert(0); + return false; } } else if (channels[c].pixel_type == TINYEXR_PIXELTYPE_FLOAT) { const float *line_ptr = reinterpret_cast( @@ -9913,6 +9957,8 @@ static void DecodePixelData(/* out */ unsigned char **out_images, } } } + + return true; } static void DecodeTiledPixelData( @@ -10161,7 +10207,12 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, // xSampling: int // ySampling: int - ReadChannelInfo(info->channels, data); + if (!ReadChannelInfo(info->channels, data)) { + if (err) { + (*err) = "Failed to parse channel info."; + } + return TINYEXR_ERROR_INVALID_DATA; + } if (info->channels.size() < 1) { if (err) { @@ -10173,16 +10224,19 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, has_channels = true; } else if (attr_name.compare("dataWindow") == 0) { - memcpy(&info->data_window[0], &data.at(0), sizeof(int)); - memcpy(&info->data_window[1], &data.at(4), sizeof(int)); - memcpy(&info->data_window[2], &data.at(8), sizeof(int)); - memcpy(&info->data_window[3], &data.at(12), sizeof(int)); - tinyexr::swap4(reinterpret_cast(&info->data_window[0])); - tinyexr::swap4(reinterpret_cast(&info->data_window[1])); - tinyexr::swap4(reinterpret_cast(&info->data_window[2])); - tinyexr::swap4(reinterpret_cast(&info->data_window[3])); - - has_data_window = true; + if (data.size() < 16) { + // Corrupsed file(Issue #50). + } else { + memcpy(&info->data_window[0], &data.at(0), sizeof(int)); + memcpy(&info->data_window[1], &data.at(4), sizeof(int)); + memcpy(&info->data_window[2], &data.at(8), sizeof(int)); + memcpy(&info->data_window[3], &data.at(12), sizeof(int)); + tinyexr::swap4(reinterpret_cast(&info->data_window[0])); + tinyexr::swap4(reinterpret_cast(&info->data_window[1])); + tinyexr::swap4(reinterpret_cast(&info->data_window[2])); + tinyexr::swap4(reinterpret_cast(&info->data_window[3])); + has_data_window = true; + } } else if (attr_name.compare("displayWindow") == 0) { memcpy(&info->display_window[0], &data.at(0), sizeof(int)); memcpy(&info->display_window[1], &data.at(4), sizeof(int)); @@ -10268,7 +10322,7 @@ static int ParseEXRHeader(HeaderInfo *info, bool *empty_header, } if (!has_data_window) { - ss_err << "\"dataWindow\" attribute not found in the header." + ss_err << "\"dataWindow\" attribute not found in the header or invalid." << std::endl; } @@ -10333,7 +10387,7 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { #else strncpy(exr_header->channels[c].name, info.channels[c].name.c_str(), 255); #endif - // manually add '\0' for safety. + // manually add '\0' for safety. exr_header->channels[c].name[255] = '\0'; exr_header->channels[c].pixel_type = info.channels[c].pixel_type; @@ -10371,7 +10425,7 @@ static void ConvertHeader(EXRHeader *exr_header, const HeaderInfo &info) { static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, const std::vector &offsets, - const unsigned char *head) { + const unsigned char *head, const size_t size) { int num_channels = exr_header->num_channels; int num_scanline_blocks = 1; @@ -10412,6 +10466,11 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, // 16 byte: tile coordinates // 4 byte : data size // ~ : data(uncompressed or compressed) + if (offsets[tile_idx] + sizeof(int) * 5 > size) { + return TINYEXR_ERROR_INVALID_DATA; + } + + size_t data_size = size - (offsets[tile_idx] + sizeof(int) * 5); const unsigned char *data_ptr = reinterpret_cast(head + offsets[tile_idx]); @@ -10430,7 +10489,10 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, memcpy(&data_len, data_ptr + 16, sizeof(int)); // 16 = sizeof(tile_coordinates) tinyexr::swap4(reinterpret_cast(&data_len)); - assert(data_len >= 4); + + if (data_len < 4 || size_t(data_len) > data_size) { + return TINYEXR_ERROR_INVALID_DATA; + } // Move to data addr: 20 = 16 + 4; data_ptr += 20; @@ -10467,11 +10529,18 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, #endif for (int y = 0; y < static_cast(num_blocks); y++) { size_t y_idx = static_cast(y); - const unsigned char *data_ptr = - reinterpret_cast(head + offsets[y_idx]); + + if (offsets[y_idx] + sizeof(int) * 2 > size) { + return TINYEXR_ERROR_INVALID_DATA; + } + // 4 byte: scan line // 4 byte: data size // ~ : pixel data(uncompressed or compressed) + size_t data_size = size - (offsets[y_idx] + sizeof(int) * 2); + const unsigned char *data_ptr = + reinterpret_cast(head + offsets[y_idx]); + int line_no; memcpy(&line_no, data_ptr, sizeof(int)); int data_len; @@ -10479,30 +10548,41 @@ static int DecodeChunk(EXRImage *exr_image, const EXRHeader *exr_header, tinyexr::swap4(reinterpret_cast(&line_no)); tinyexr::swap4(reinterpret_cast(&data_len)); + if (size_t(data_len) > data_size) { + return TINYEXR_ERROR_INVALID_DATA; + } + int end_line_no = (std::min)(line_no + num_scanline_blocks, (exr_header->data_window[3] + 1)); int num_lines = end_line_no - line_no; - assert(num_lines > 0); - - // Move to data addr: 8 = 4 + 4; - data_ptr += 8; - - // Adjust line_no with data_window.bmin.y - line_no -= exr_header->data_window[1]; + //assert(num_lines > 0); - if (line_no < 0) { + if (num_lines <= 0) { invalid_data = true; } else { - tinyexr::DecodePixelData( - 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, num_lines, static_cast(pixel_data_size), - static_cast(exr_header->num_custom_attributes), - exr_header->custom_attributes, - static_cast(exr_header->num_channels), exr_header->channels, - channel_offset_list); + + // Move to data addr: 8 = 4 + 4; + data_ptr += 8; + + // Adjust line_no with data_window.bmin.y + line_no -= exr_header->data_window[1]; + + if (line_no < 0) { + invalid_data = true; + } else { + if (!tinyexr::DecodePixelData( + 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, num_lines, static_cast(pixel_data_size), + static_cast(exr_header->num_custom_attributes), + exr_header->custom_attributes, + static_cast(exr_header->num_channels), exr_header->channels, + channel_offset_list)) { + invalid_data = true; + } + } } } // omp parallel } @@ -10537,7 +10617,7 @@ static bool ReconstructLineOffsets( for (size_t i = 0; i < n; i++) { size_t offset = static_cast(marker - head); // Offset should not exceed whole EXR file/data size. - if (offset >= size) { + if ((offset + sizeof(tinyexr::tinyexr_uint64)) >= size) { return false; } @@ -10586,8 +10666,15 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, int data_width = exr_header->data_window[2] - exr_header->data_window[0] + 1; int data_height = exr_header->data_window[3] - exr_header->data_window[1] + 1; + if ((data_width < 0) || (data_height < 0)) { + if (err) { + (*err) = "Invalid data window value."; + } + return TINYEXR_ERROR_INVALID_DATA; + } + // Read offset tables. - size_t num_blocks; + size_t num_blocks = 0; if (exr_header->chunk_count > 0) { // Use `chunkCount` attribute. @@ -10657,7 +10744,7 @@ static int DecodeEXRImage(EXRImage *exr_image, const EXRHeader *exr_header, } } - return DecodeChunk(exr_image, exr_header, offsets, head); + return DecodeChunk(exr_image, exr_header, offsets, head, size); } } // namespace tinyexr @@ -10842,8 +10929,8 @@ int ParseEXRHeaderFromMemory(EXRHeader *exr_header, const EXRVersion *version, } int LoadEXRFromMemory(float **out_rgba, int *width, int *height, - const unsigned char *memory, size_t size, - const char **err) { + const unsigned char *memory, size_t size, + const char **err) { if (out_rgba == NULL || memory == NULL) { if (err) { (*err) = "Invalid argument.\n"; @@ -10866,13 +10953,13 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, if (ret != TINYEXR_SUCCESS) { return ret; } - + // Read HALF channel as FLOAT. for (int i = 0; i < exr_header.num_channels; i++) { if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) { exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; } - } + } InitEXRImage(&exr_image); ret = LoadEXRImageFromMemory(&exr_image, &exr_header, memory, size, err); @@ -10923,23 +11010,22 @@ int LoadEXRFromMemory(float **out_rgba, int *width, int *height, } (*out_rgba) = reinterpret_cast( - malloc(4 * sizeof(float) * static_cast(exr_image.width) * - static_cast(exr_image.height))); + malloc(4 * sizeof(float) * static_cast(exr_image.width) * + static_cast(exr_image.height))); for (int i = 0; i < exr_image.width * exr_image.height; i++) { - (*out_rgba)[4 * i + 0] = - reinterpret_cast(exr_image.images)[idxR][i]; - (*out_rgba)[4 * i + 1] = - reinterpret_cast(exr_image.images)[idxG][i]; - (*out_rgba)[4 * i + 2] = - reinterpret_cast(exr_image.images)[idxB][i]; - if (idxA != -1) { - (*out_rgba)[4 * i + 3] = - reinterpret_cast(exr_image.images)[idxA][i]; - } - else { - (*out_rgba)[4 * i + 3] = 1.0; - } + (*out_rgba)[4 * i + 0] = + reinterpret_cast(exr_image.images)[idxR][i]; + (*out_rgba)[4 * i + 1] = + reinterpret_cast(exr_image.images)[idxG][i]; + (*out_rgba)[4 * i + 2] = + reinterpret_cast(exr_image.images)[idxB][i]; + if (idxA != -1) { + (*out_rgba)[4 * i + 3] = + reinterpret_cast(exr_image.images)[idxA][i]; + } else { + (*out_rgba)[4 * i + 3] = 1.0; + } } (*width) = exr_image.width; @@ -11707,7 +11793,12 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { // xSampling: int // ySampling: int - tinyexr::ReadChannelInfo(channels, data); + if (!tinyexr::ReadChannelInfo(channels, data)) { + if (err) { + (*err) = "Failed to parse channel info."; + } + return TINYEXR_ERROR_INVALID_DATA; + } num_channels = static_cast(channels.size()); @@ -11844,9 +11935,11 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { { unsigned long dstLen = static_cast(pixelOffsetTable.size() * sizeof(int)); - tinyexr::DecompressZip( + if (!tinyexr::DecompressZip( reinterpret_cast(&pixelOffsetTable.at(0)), &dstLen, - data_ptr + 28, static_cast(packedOffsetTableSize)); + data_ptr + 28, static_cast(packedOffsetTableSize))) { + return false; + } assert(dstLen == pixelOffsetTable.size() * sizeof(int)); for (size_t i = 0; i < static_cast(data_width); i++) { @@ -11861,10 +11954,12 @@ int LoadDeepEXR(DeepImage *deep_image, const char *filename, const char **err) { { unsigned long dstLen = static_cast(unpackedSampleDataSize); if (dstLen) { - tinyexr::DecompressZip( + if (!tinyexr::DecompressZip( reinterpret_cast(&sample_data.at(0)), &dstLen, data_ptr + 28 + packedOffsetTableSize, - static_cast(packedSampleDataSize)); + static_cast(packedSampleDataSize))) { + return false; + } assert(dstLen == static_cast(unpackedSampleDataSize)); } } @@ -12390,7 +12485,7 @@ int LoadEXRMultipartImageFromMemory(EXRImage *exr_images, } int ret = tinyexr::DecodeChunk(&exr_images[i], exr_headers[i], offset_table, - memory); + memory, size); if (ret != TINYEXR_SUCCESS) { return ret; }