diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index 0603a25d08..26539321fe 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -31,6 +31,7 @@ #include "Common/StringUtils.h" #include "Core/System.h" +#include "Core/Util/PathUtil.h" #include "GPU/Common/PostShader.h" static std::vector shaderInfo; @@ -154,12 +155,17 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct section.Get("Name", &info.name); section.Get("Parent", &info.parent); section.Get("Visible", &info.visible); - temp.clear(); - section.Get("Fragment", &temp); - info.fragmentShaderFile = path / temp; - temp.clear(); - section.Get("Vertex", &temp); - info.vertexShaderFile = path / temp; + std::string fragment; + std::string vertex; + section.Get("Fragment", &fragment); + section.Get("Vertex", &vertex); + // Shader inis come from downloads, so keep them to their own folder. + if (HasParentDirComponent(fragment) || HasParentDirComponent(vertex)) { + WARN_LOG(Log::G3D, "Ignoring shader '%s' with a parent dir in its paths", info.section.c_str()); + continue; + } + info.fragmentShaderFile = path / fragment; + info.vertexShaderFile = path / vertex; section.Get("OutputResolution", &info.outputResolution); section.Get("Upscaling", &info.isUpscalingFilter); section.Get("SSAA", &info.SSAAFilterLevel); @@ -221,19 +227,33 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector &direct section.Get("Hidden", &info.hidden); std::string cbufferFilename; if (section.Get("ConstantBuffer", &cbufferFilename)) { + if (HasParentDirComponent(cbufferFilename)) { + WARN_LOG(Log::G3D, "Ignoring texture shader '%s' with a parent dir in its paths", info.section.c_str()); + continue; + } Path cbufferPath = path / cbufferFilename; info.constantBuffer = cbufferPath; } if (section.Get("Compute", &temp)) { + if (HasParentDirComponent(temp)) { + WARN_LOG(Log::G3D, "Ignoring texture shader '%s' with a parent dir in its paths", info.section.c_str()); + continue; + } info.computeShaderFile = path / temp; info.computeShaderFiles.push_back(info.computeShaderFile); + bool badPath = false; for (int computeIndex = 2; computeIndex <= 4; ++computeIndex) { temp.clear(); if (section.Get(StringFromFormat("Compute%d", computeIndex).c_str(), &temp)) { + if (HasParentDirComponent(temp)) { + badPath = true; + } info.computeShaderFiles.push_back(path / temp); } } - if (info.scaleFactor >= 2 && info.scaleFactor < 8) { + if (badPath) { + WARN_LOG(Log::G3D, "Ignoring texture shader '%s' with a parent dir in its paths", info.section.c_str()); + } else if (info.scaleFactor >= 2 && info.scaleFactor < 8) { appendTextureShader(info); } } else { diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp index 6a77d4b698..1ff28e5753 100644 --- a/GPU/Common/ReplacedTexture.cpp +++ b/GPU/Common/ReplacedTexture.cpp @@ -1015,9 +1015,21 @@ bool ReplacedTexture::CopyLevelTo(int level, uint8_t *out, size_t outDataSize, i WARN_LOG(Log::TexReplacement, "Level %d is empty", level); return false; } + if (info.w > outW || info.h > outH) { + WARN_LOG(Log::TexReplacement, "Level %d (%dx%d) is bigger than its padded size %dx%d", level, info.w, info.h, outW, outH); + return false; + } int blockSize; - if (!Draw::DataFormatIsBlockCompressed(fmt, &blockSize)) { + const bool blockCompressed = Draw::DataFormatIsBlockCompressed(fmt, &blockSize); + const size_t needed = blockCompressed ? (size_t)((outW + 3) / 4) * ((outH + 3) / 4) * blockSize : (size_t)rowPitch * (outH - 1) + outW * 4; + // A zero size disables the check, like for the other copies. + if (outDataSize != 0 && outDataSize < needed) { + WARN_LOG(Log::TexReplacement, "Level %d doesn't fit the buffer (%d < %d)", level, (int)outDataSize, (int)needed); + return false; + } + + if (!blockCompressed) { if (fmt != Draw::DataFormat::R8G8B8A8_UNORM) { ERROR_LOG(Log::TexReplacement, "Unexpected linear data format"); return false; diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index b143de8e11..96087d883e 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -413,8 +413,9 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri std::string_view v = line.Value(); if (sscanf(k, "%16llx%8x_%d", &key.cachekey, &key.hash, &level) >= 1) { // Reject path traversal: a "../" component could make us read - // or write files outside the texture pack directory. - if (HasParentDirComponent(v)) { + // or write files outside the texture pack directory. '|' separates mip levels in the alias, + // so it would let a later level skip this check. + if (HasParentDirComponent(v) || v.find('|') != std::string_view::npos) { ERROR_LOG(Log::TexReplacement, "Ignoring texture filename with parent dir component: %s", std::string(v).c_str()); continue; } @@ -521,6 +522,12 @@ void TextureReplacer::ParseHashRange(const std::string &key, const std::string & return; } + // Generous, PSP textures are at most 512x512. Values of 65536 or more would also spill into the other fields of the key. + if (fromW > 1024 || fromH > 1024) { + ERROR_LOG(Log::TexReplacement, "Ignoring invalid hashrange %s = %s, source bigger than 1024x1024", key.c_str(), value.c_str()); + return; + } + if (toW > fromW || toH > fromH) { ERROR_LOG(Log::TexReplacement, "Ignoring invalid hashrange %s = %s, range bigger than source", key.c_str(), value.c_str()); return; @@ -934,13 +941,13 @@ void TextureReplacer::NotifyTextureDecoded(ReplacedTexture *texture, const Repla // Only save the hashed portion of the PNG, in case we have specified it in the ini file. int lookupW; int lookupH; - if (LookupHashRange(replacedInfo.addr, origW, origH, &lookupW, &lookupH)) { + if (LookupHashRange(replacedInfo.addr, origW, origH, &lookupW, &lookupH) && lookupW > 0 && lookupH > 0 && lookupW <= origW && lookupH <= origH) { w = lookupW * (scaledW / origW); h = lookupH * (scaledH / origH); } constexpr int bpp = 4; - size_t saveBufSize = w * h * bpp; + size_t saveBufSize = (size_t)w * h * bpp; u8 *saveBuf = (u8 *)malloc(saveBufSize); if (!saveBuf) { ERROR_LOG(Log::TexReplacement, "Failed to allocated %d bytes of memory for saving a texture", (int)saveBufSize); @@ -950,7 +957,7 @@ void TextureReplacer::NotifyTextureDecoded(ReplacedTexture *texture, const Repla // Copy data to a buffer so we can send it to the thread. Might as well compact-away the pitch // while we're at it. for (int y = 0; y < h; y++) { - memcpy(saveBuf + y * w * bpp, (const u8 *)data + y * srcPitch, w * bpp); + memcpy(saveBuf + (size_t)y * w * bpp, (const u8 *)data + (size_t)y * srcPitch, w * bpp); } SaveTextureTask *task = new SaveTextureTask(std::move(saveBuf)); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 1b07e163ae..1df467ec60 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -366,6 +366,13 @@ bool TextureCacheVulkan::LoadConstantBuffer(VulkanContext *vulkan, VkCommandBuff ClearScalingShaders(vulkan); return false; } + // The file comes with a downloaded shader, and has to make a valid uniform buffer. + if (constantsSize < 16 || (constantsSize & 15) != 0 || constantsSize > vulkan->GetPhysicalDeviceProperties().properties.limits.maxUniformBufferRange) { + ERROR_LOG(Log::G3D, "Constant buffer file '%s' has a bad size (%d) - disabling hardware scaling", cbufferPath_.c_str(), (int)constantsSize); + delete[] contents; + ClearScalingShaders(vulkan); + return false; + } textureScaleCBuffer_.Create(vulkan, "TextureScale CBuffer", constantsSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT); VulkanPushPool *pushPool = drawEngine_->GetPushBufferForTextureData(); VkBuffer srcBuf = VK_NULL_HANDLE;