From 9ad3b821d9d29c76b49f79e324d7847cb23977f7 Mon Sep 17 00:00:00 2001 From: Acts1631 Date: Thu, 3 Sep 2026 10:07:13 -0400 Subject: [PATCH 1/2] Bound replacement PNG dimensions safely Reject non-positive or oversized dimensions independently in the PNG header peeker, matching the existing 8192 pixel decode limit. Use checked size_t arithmetic before allocating replacement RGBA data so crafted dimensions cannot overflow the allocation size. --- Common/Data/Format/PNGLoad.cpp | 8 ++++++-- GPU/Common/ReplacedTexture.cpp | 12 ++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Common/Data/Format/PNGLoad.cpp b/Common/Data/Format/PNGLoad.cpp index 6f843ba319..7294d049dd 100644 --- a/Common/Data/Format/PNGLoad.cpp +++ b/Common/Data/Format/PNGLoad.cpp @@ -148,8 +148,12 @@ bool PNGHeaderPeek::IsValidPNGHeader() const { if (magic != 0x474e5089 || ihdrTag != 0x52444849) { return false; } - // Reject crazy sized images, too. - if (Width() > 32768 && Height() > 32768) { + // Keep the peeker's limit consistent with pngLoadPtr(). A replacement + // texture is decoded into an uncompressed RGBA buffer, so accepting one + // oversized dimension would still allow a decompression bomb. + const int width = Width(); + const int height = Height(); + if (width <= 0 || height <= 0 || width > 8192 || height > 8192) { return false; } return true; diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index 0b8b7f2d00..6d2dfa05dd 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "ppsspp_config.h" @@ -721,8 +722,15 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference png.format = PNG_FORMAT_RGBA; std::vector &out = data_[mipLevel]; - // TODO: Should probably try to handle out-of-memory gracefully here. - out.resize(level.w * level.h * 4); + const size_t width = (size_t)level.w; + const size_t height = (size_t)level.h; + if (level.w <= 0 || level.h <= 0 || + width > std::numeric_limits::max() / height || + width * height > std::numeric_limits::max() / 4) { + ERROR_LOG(Log::TexReplacement, "PNG replacement dimensions are too large: %s (%dx%d)", filename.c_str(), level.w, level.h); + return LoadLevelResult::LOAD_ERROR; + } + out.resize(width * height * 4); if (!png_image_finish_read(&png, nullptr, &out[0], level.w * 4, nullptr)) { ERROR_LOG(Log::TexReplacement, "Could not load texture replacement: %s - %s", filename.c_str(), png.message); out.resize(0); From 90f7c6d1956d931f95c0dfcb55e4884f584bb994 Mon Sep 17 00:00:00 2001 From: Acts1631 Date: Mon, 7 Sep 2026 14:52:06 -0400 Subject: [PATCH 2/2] Rely on PNG header dimension bounds PNG replacement dimensions are validated by PNGHeaderPeek before the decoded buffer is allocated, so the additional size_t overflow checks are redundant. --- GPU/Common/ReplacedTexture.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index 6d2dfa05dd..79c1c80e21 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -16,7 +16,6 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include -#include #include "ppsspp_config.h" @@ -722,15 +721,7 @@ ReplacedTexture::LoadLevelResult ReplacedTexture::LoadLevelData(VFSFileReference png.format = PNG_FORMAT_RGBA; std::vector &out = data_[mipLevel]; - const size_t width = (size_t)level.w; - const size_t height = (size_t)level.h; - if (level.w <= 0 || level.h <= 0 || - width > std::numeric_limits::max() / height || - width * height > std::numeric_limits::max() / 4) { - ERROR_LOG(Log::TexReplacement, "PNG replacement dimensions are too large: %s (%dx%d)", filename.c_str(), level.w, level.h); - return LoadLevelResult::LOAD_ERROR; - } - out.resize(width * height * 4); + out.resize(level.w * level.h * 4); if (!png_image_finish_read(&png, nullptr, &out[0], level.w * 4, nullptr)) { ERROR_LOG(Log::TexReplacement, "Could not load texture replacement: %s - %s", filename.c_str(), png.message); out.resize(0);