diff --git a/GPU/Common/TextureReplacer.cpp b/GPU/Common/TextureReplacer.cpp index 90a9f8b8ab..b143de8e11 100644 --- a/GPU/Common/TextureReplacer.cpp +++ b/GPU/Common/TextureReplacer.cpp @@ -367,6 +367,9 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri } options->Get("skipLastDXT1Blocks128x64", &skipLastDXT1Blocks128x64_); options->Get("skipLastDXT1Blocks128x128", &skipLastDXT1Blocks128x128_); + // These are subtracted from the hashed size, so can't be more than the blocks there are. + skipLastDXT1Blocks128x64_ = std::clamp(skipLastDXT1Blocks128x64_, 0, (128 / 4) * (64 / 4)); + skipLastDXT1Blocks128x128_ = std::clamp(skipLastDXT1Blocks128x128_, 0, (128 / 4) * (128 / 4)); if (reduceHash_ && textureHash_ == ReplacedTextureHash::QUICK) { reduceHash_ = false; ERROR_LOG(Log::TexReplacement, "Texture Replacement: reduceHash option requires safer hash, use xxh32 or xxh64 instead."); @@ -635,11 +638,12 @@ u32 TextureReplacer::ComputeHash(u32 addr, int bufw, int w, int h, bool swizzled } // Hack for Yu Gi Oh texture hashing problem. See issue #19714 - if (skipLastDXT1Blocks128x64_ && fmt == GE_TFMT_DXT1 && w == 128 && h == 64) { + // (reduceHash can make sizeInRAM smaller than all the blocks, so check it can't underflow.) + if (skipLastDXT1Blocks128x64_ && fmt == GE_TFMT_DXT1 && w == 128 && h == 64 && (u32)(8 * skipLastDXT1Blocks128x64_) < (u32)sizeInRAM) { // Skip the last few blocks as specified. sizeInRAM -= 8 * skipLastDXT1Blocks128x64_; } - if (skipLastDXT1Blocks128x128_ && fmt == GE_TFMT_DXT1 && w == 128 && h == 128) { + if (skipLastDXT1Blocks128x128_ && fmt == GE_TFMT_DXT1 && w == 128 && h == 128 && (u32)(8 * skipLastDXT1Blocks128x128_) < (u32)sizeInRAM) { sizeInRAM -= 8 * skipLastDXT1Blocks128x128_; } diff --git a/UI/BackgroundAudio.cpp b/UI/BackgroundAudio.cpp index 36f8322b64..ffc475cd5c 100644 --- a/UI/BackgroundAudio.cpp +++ b/UI/BackgroundAudio.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "Common/File/VFS/VFS.h" #include "Common/UI/Root.h" @@ -132,14 +133,19 @@ bool WavData::Read(RIFFReader &file_) { int samplesPerFrame = codec == PSP_CODEC_AT3PLUS ? 2048 : 1024; for (int i = 0; i < numLoops; ++i) { - // Only seen forward loops, so let's ignore others. - if (loops[i].type != 0) + // Only seen forward loops, so let's ignore others. Also ones that don't go forwards, + // which would point playback before the data. + if (loops[i].type != 0 || loops[i].startSample < 0 || loops[i].endSample <= loops[i].startSample) continue; // We ignore loop interpolation (fraction) and play count for now. - raw_offset_loop_start = (loops[i].startSample / samplesPerFrame) * raw_bytes_per_frame; + const s64 loopStart = (s64)(loops[i].startSample / samplesPerFrame) * raw_bytes_per_frame; + const s64 loopEnd = (s64)(loops[i].endSample / samplesPerFrame) * raw_bytes_per_frame; + if (loopEnd > INT_MAX) + continue; + raw_offset_loop_start = (int)loopStart; loop_start_offset = loops[i].startSample % samplesPerFrame; - raw_offset_loop_end = (loops[i].endSample / samplesPerFrame) * raw_bytes_per_frame; + raw_offset_loop_end = (int)loopEnd; loop_end_offset = loops[i].endSample % samplesPerFrame; if (loops[i].playCount == 0) { @@ -201,6 +207,14 @@ bool WavData::Read(RIFFReader &file_) { ERROR_LOG(Log::Audio, "Could not descend into RIFF file."); return false; } + // The loop points came from the smpl chunk, so they have to land in the data. The sample + // offsets (within a frame) are always positive, since the loop samples were checked above. + if (raw_offset_loop_start < 0 || raw_offset_loop_end > raw_data_size || raw_offset_loop_start > raw_offset_loop_end) { + raw_offset_loop_start = 0; + raw_offset_loop_end = 0; + loop_start_offset = 0; + loop_end_offset = 0; + } sample_rate = samplesPerSec; return true; } diff --git a/UI/ImposeHome.cpp b/UI/ImposeHome.cpp index b4f8fa1af5..dfc99e7160 100644 --- a/UI/ImposeHome.cpp +++ b/UI/ImposeHome.cpp @@ -298,7 +298,7 @@ bool ImposeArt::Parse(const std::vector &file, int pspLanguage) { const uint32_t w = r.U32(rec + 16); const uint32_t h = r.U32(rec + 20); const uint32_t offset = r.U32(rec + 24); - if (!r.ok || palette >= NUM_PALETTES || w == 0 || h == 0 || x + w > PSP_W || y + h > PSP_H || (w * h + 1) / 2 != size) { + if (!r.ok || palette >= NUM_PALETTES || w == 0 || h == 0 || w > PSP_W || x > PSP_W - w || h > PSP_H || y > PSP_H - h || (w * h + 1) / 2 != size) { return false; } const size_t pixels = block + TEXT_RECORDS + (size_t)offset; diff --git a/UI/Store.cpp b/UI/Store.cpp index 1e8e67529d..65cbb8c8a2 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -448,7 +448,9 @@ void StoreScreen::ParseListing(const std::string &json) { // (Path::operator/ does no ".." normalization); on platforms/builds where // the store index isn't fetched over HTTPS, a MITM'd or compromised // response could otherwise point "launch" at an arbitrary host path. - if (HasParentDirComponent(file)) + // It's also uninstalled (deleted recursively) by name, so "." or "/" would be the + // whole GAME directory. + if (HasParentDirComponent(file) || !IsSafePathComponent(file)) continue; e.file = file; entries_.push_back(e); diff --git a/headless/Compare.cpp b/headless/Compare.cpp index 16e7897dad..e35e9a667d 100644 --- a/headless/Compare.cpp +++ b/headless/Compare.cpp @@ -403,6 +403,13 @@ double ScreenshotComparer::Compare(const Path &screenshotFilename) { return -1.0f; } + // Everything below reads w_ x h_ pixels from it. + if (width < (int)w_ || height < (int)h_) { + error_ = StringFromFormat("Screenshot is too small (%dx%d, expected %dx%d): %s", width, height, (int)w_, (int)h_, screenshotFilename.ToVisualString().c_str()); + free(reference_); + reference_ = nullptr; + return -1.0f; + } referenceStride_ = width; } } else {