mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
Texture packs, shaders: Bound what shared content can ask for
- textures.ini hashranges with a source over 1024 are rejected. A height of 65536 or more spilled into the width bits of the key, matching a real texture with a range bigger than it, which overflowed the upload buffer in CopyLevelTo and the save buffer in NotifyTextureDecoded. Both now also check the sizes themselves. - A '|' in a textures.ini filename split into another mip level after the parent dir check, so it's rejected. - Post-processing and texture shader inis can't point outside their own folder, and a texture shader's ConstantBuffer must be a valid uniform buffer size. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
05cca98c31
commit
ceab9b7a3f
4 files changed
+59
-13
No files matched your search
@@ -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> shaderInfo;
|
||||
@@ -154,12 +155,17 @@ void LoadPostShaderInfo(Draw::DrawContext *draw, const std::vector<Path> &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<Path> &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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user