diff --git a/Common/Data/Format/ZIMLoad.cpp b/Common/Data/Format/ZIMLoad.cpp index daace4992d..0113a46d71 100644 --- a/Common/Data/Format/ZIMLoad.cpp +++ b/Common/Data/Format/ZIMLoad.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "zlib.h" @@ -50,6 +51,10 @@ int ezuncompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSr } int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int *flags, uint8_t **image) { + if (datasize < 16) { + ERROR_LOG(Log::IO, "ZIM file is too small"); + return 0; + } if (zim[0] != 'Z' || zim[1] != 'I' || zim[2] != 'M' || zim[3] != 'G') { ERROR_LOG(Log::IO, "Not a ZIM file"); return 0; @@ -58,29 +63,53 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int memcpy(height, zim + 8, 4); memcpy(flags, zim + 12, 4); + if (*width <= 0 || *height <= 0) { + ERROR_LOG(Log::IO, "Invalid ZIM dimensions %i x %i", *width, *height); + return 0; + } + int num_levels = 1; - int image_data_size[ZIM_MAX_MIP_LEVELS]{}; + size_t image_data_size[ZIM_MAX_MIP_LEVELS]{}; if (*flags & ZIM_HAS_MIPS) { num_levels = log2i(*width < *height ? *width : *height) + 1; } - int total_data_size = 0; + if (num_levels > ZIM_MAX_MIP_LEVELS) { + ERROR_LOG(Log::IO, "Too many ZIM mip levels: %i", num_levels); + return 0; + } + + size_t total_data_size = 0; for (int i = 0; i < num_levels; i++) { if (i > 0) { width[i] = width[i-1] / 2; height[i] = height[i-1] / 2; } + + const size_t level_width = (size_t)width[i]; + const size_t level_height = (size_t)height[i]; + size_t bytes_per_pixel; switch (*flags & ZIM_FORMAT_MASK) { case ZIM_RGBA8888: - image_data_size[i] = width[i] * height[i] * 4; + bytes_per_pixel = 4; break; case ZIM_RGBA4444: case ZIM_RGB565: - image_data_size[i] = width[i] * height[i] * 2; + bytes_per_pixel = 2; break; default: ERROR_LOG(Log::IO, "Invalid ZIM format %i", *flags & ZIM_FORMAT_MASK); return 0; } + if (level_width > std::numeric_limits::max() / level_height || + level_width * level_height > std::numeric_limits::max() / bytes_per_pixel) { + ERROR_LOG(Log::IO, "ZIM image size is too large"); + return 0; + } + image_data_size[i] = level_width * level_height * bytes_per_pixel; + if (total_data_size > std::numeric_limits::max() - image_data_size[i]) { + ERROR_LOG(Log::IO, "ZIM mip data size is too large"); + return 0; + } total_data_size += image_data_size[i]; } @@ -89,14 +118,29 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int return 0; } + const size_t payload_size = datasize - 16; + if (!(*flags & (ZIM_ZLIB_COMPRESSED | ZIM_ZSTD_COMPRESSED)) && payload_size != total_data_size) { + ERROR_LOG(Log::IO, "Wrong size data in ZIM: %i vs %i", (int)payload_size, (int)total_data_size); + return 0; + } + if ((*flags & ZIM_ZLIB_COMPRESSED) && + (total_data_size > (size_t)std::numeric_limits::max() || payload_size > (size_t)std::numeric_limits::max())) { + ERROR_LOG(Log::IO, "ZIM zlib data is too large"); + return 0; + } + image[0] = (uint8_t *)malloc(total_data_size); + if (!image[0]) { + ERROR_LOG(Log::IO, "Failed to allocate %lld bytes for ZIM image", (long long)total_data_size); + return 0; + } for (int i = 1; i < num_levels; i++) { image[i] = image[i-1] + image_data_size[i-1]; } if (*flags & ZIM_ZLIB_COMPRESSED) { long outlen = (long)total_data_size; - int retcode = ezuncompress(*image, &outlen, (unsigned char *)(zim + 16), (long)datasize - 16); + int retcode = ezuncompress(*image, &outlen, (unsigned char *)(zim + 16), (long)payload_size); if (Z_OK != retcode) { ERROR_LOG(Log::IO, "ZIM zlib format decompression failed: %d", retcode); free(*image); @@ -108,7 +152,7 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int ERROR_LOG(Log::IO, "Wrong size data in ZIM: %i vs %i", (int)outlen, (int)total_data_size); } } else if (*flags & ZIM_ZSTD_COMPRESSED) { - size_t outlen = ZSTD_decompress(*image, total_data_size, zim + 16, datasize - 16); + size_t outlen = ZSTD_decompress(*image, total_data_size, zim + 16, payload_size); if (outlen != (size_t)total_data_size) { ERROR_LOG(Log::IO, "ZIM zstd format decompression failed: %lld", (long long)outlen); free(*image); @@ -116,10 +160,7 @@ int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int return 0; } } else { - memcpy(*image, zim + 16, datasize - 16); - if (datasize - 16 != (size_t)total_data_size) { - ERROR_LOG(Log::IO, "Wrong size data in ZIM: %i vs %i", (int)(datasize-16), (int)total_data_size); - } + memcpy(*image, zim + 16, payload_size); } return num_levels; }