3590 Commits
Author SHA1 Message Date
Henrik Rydgård 293cf0c78c Revert the logic changes from "Add new TexCache logging channel"
This partially reverts commit ee1314f803.
2026-09-02 15:56:10 -06:00
Henrik Rydgård d4b6965db0 TestBoundingBox: bail out when the indices reach past the scratch space
corners and verts are carved out of decoded_ at fixed offsets 6*65536 bytes apart,
and NormalizeVertices fills corners with indexUpperBound - indexLowerBound + 1
SimpleVertex. The vertexCount > 1024 guard doesn't bound that: the index values come
from the game, so 1024 indices can span the full 16-bit range and run corners into
verts, making the cull decision from overwritten data. Bail on an index over 1024
and report visible - a bbox test that large isn't worth doing anyway.
2026-09-02 18:06:47 +02:00
Henrik Rydgård 506cfb6701 Bound two values that texture packs and shader inis supply
A hashrange of 'addr,w,h = 0,0' passed validation (0 isn't bigger than the source),
became desc_.newW/newH, and ReplacedTexture::Prepare divides by them. A post-shader
SSAA level multiplies the render resolution with no upper bound, while the
texture-shader Scale sitting a few lines away is checked against 2..8.
2026-09-02 18:06:47 +02:00
Henrik Rydgård 56bba5f6f5 Merge pull request #22189 from hrydgard/debug-input-rewind-overflows
Fix three more out-of-bounds writes (minor)
2026-08-31 17:53:10 +02:00
Henrik Rydgård 9cb50459d6 Merge pull request #22186 from hrydgard/medium-correctness-fixes
Medium-severity correctness fixes
2026-08-31 16:30:53 +02:00
Henrik Rydgård 1ee9168711 Merge pull request #22184 from hrydgard/fix-adreno-workaround-regression
Fix fragment shader logic error (Adreno stencil driver bug workaround)
2026-08-31 13:30:19 +02:00
Henrik Rydgård 496eddb7fb Merge pull request #22185 from hrydgard/framebuffer-and-null-deref-fixes
Framebuffer and null deref fixes
2026-08-31 13:29:40 +02:00
Henrik Rydgård 7604ae67ff FramebufferManagerCommon: don't deref dstRect.vfb when no dst buffer was found
A failed FindTransferFramebuffer leaves dstRect zero-initialized, and RASTER_COLOR is
0, so the channel check passes and vfb is read through a null pointer. Only reachable
for a depth-source transfer to an address with no matching framebuffer.
Check dstBuffer first, like every other use of dstRect.vfb below.
2026-08-31 13:02:53 +02:00
Henrik Rydgård a99ab6f6f6 TextureReplacer: reject traversal in the override ini name, fix dangling vfs_
The per-game override filename from [games] went straight to LoadFromVFS with no
check, while the [hashes] filenames and ReplacedTexture::Prepare both run theirs
through HasParentDirComponent. For a directory-backed pack DirectoryReader resolves
it against the pack directory, so '../../..' reads anything on disk - and texture
packs are third-party downloads. Check it the same way. (Zip-backed packs weren't
affected.)

Turning replacement off mid-session did 'delete vfs_; vfs_ = nullptr;' without
updating the cached ReplacedTextures that hold the same pointer - LoadIni has a fixup
loop for exactly this when it swaps the VFS, and the disable path needed one too.
Decimate(ALL) right after doesn't help: it only frees their data, it doesn't erase
the entries. A texture still PENDING (or one whose try_lock in Decimate failed) then
used the freed VFS from a worker thread, or from ~ReplacedTexture's ReleaseFile.
Clear the back-pointers, and make the destructor and Prepare() tolerate a null one.
2026-08-31 12:58:47 +02:00
Henrik Rydgård 838cf82e04 Fix fragment shader logic error (Adreno stencil driver bug workaround)
This regressed recently. 0eede05f5f / f8b153ba2b
2026-08-31 12:41:18 +02:00
Henrik Rydgård 9b896e0f7d Merge pull request #22173 from hrydgard/range-validation-fixes
Claude code review: Fix a batch of missing or wrong range validation
2026-08-31 12:32:26 +02:00
Henrik Rydgård 98e8ffe7cd Check framebuffer copy sources, and fix two easy crashes
The three framebuffer upload paths took Memory::GetPointerUnchecked() on a
GE-supplied source address and then read height rows of it, without ever checking
that span was mapped. Only the destination was validated (and DoBlockTransfer's own
memcpy is carefully guarded, so the intent was clearly there). A copy whose source
starts near the end of RAM walks straight off the end of the view. Clamp the row
count to what's actually mapped, and warn when we do.

GhidraClient dereferenced getArray()->value for both "symbols" and "types" without a
null check, and the getTag() test underneath could never catch it - getArray() has
already filtered by tag, so it returns either a JSON_ARRAY node or nullptr. Any
HTTP 200 that parses as JSON but isn't the shape we expect - {}, a bare array, an
incompatible ghidra-rest-api, or the host/port pointed at some other JSON service -
crashed the worker thread. FetchTypes() runs first, so that's the one you'd hit.

RiscV and LoongArch CPU detection divided TotalLogicalCount() by ProcessorCount()
before checking it. ProcessorCount() returns 0 whenever /proc/cpuinfo can't be read
or doesn't parse, which is SIGFPE during static init of the cpu_info global - before
anything could handle it. The existing <= 0 guard sat after the division.

314 pspautotests pass; frametests show the same 3 pre-existing failures as master.
2026-08-31 12:15:50 +02:00
Henrik RydgårdandClaude Opus 5 016f976b1f Fix a batch of missing or wrong range validation
Memory::IsValidAddress and friends tested the extended-RAM range with
(address & 0x3F000000), i.e. at 16MB granularity, so they accepted the whole 16MB
block containing the end of RAM. That's harmless at 32MB and 64MB, but the Sora no
Kiseki SC/3rd HD remasters run with 0x04C00000, so addresses from 0x0CC00000 to
0x0CFFFFFF read as valid, and MaxSizeAtAddress then underflowed to ~4GB there -
which defeats ClampValidSizeAt and IsValidRange entirely for that window. Mask
with 0x3FFFFFFF instead, in all five helpers and the copies in MemMapFunctions.cpp.

IsValidTextureAddress's extended-RAM branch repeated the first branch's whole mask
rather than just its alignment bits, so it was dead code and extended RAM was never
accepted as a texture source.

ComputeTextureHash checked IsValidAddress(addr + sizeInRAM), i.e. only the end
address, which can land in a different valid region than the start - a VRAM texture
with a large enough computed size ends exactly at the base of RAM and "passes"
while reading far past the 8MB VRAM view. Use IsValidRange.

TextureReplacer::ComputeHash's strided path had no range check at all, unlike the
contiguous path right above it. Also clamp the pack-supplied reduce-hash factor to
1.0 - it's a reduction, and the ini parser only rejects exactly 0.

ZipExtractFileToMemory read an uninitialized zip_stat when zip_stat_index failed
(it ignored the return value) and sized a host allocation directly from the zip's
declared uncompressed size. Reached just by opening an archive.

Memory::Reinit ignored Init()'s return value, and DoState fed it a memory size
taken straight from the savestate. A bogus size made the map fail to allocate and
left base null, after which DoMemoryVoid wrote RAM through it. Validate the size,
propagate the failure, and roll back to the previous size if reinit fails.

314 pspautotests pass, all unit tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
2026-08-30 23:05:04 +02:00
Henrik RydgårdandClaude Opus 5 9f4dcf359b Fix three more out-of-bounds writes
GetCurrentDrawAsDebugVertices (GE debugger vertex preview) sized its index scratch
buffer at a fixed 65536 and then ran both expanding steps into it: index generation
turns strips/fans into up to 3 indices per input index, and RunSoftwareTransform can
then expand points/lines/rects into 6 more each. A 30000-vertex triangle strip wrote
~90000 entries. Size the buffer from the count instead.

The Expand{Rectangles,Lines,Points} capacity checks were also off: they compared the
expansion against indsSize but write the expanded indices at inds + vertexCount, so
the input count has to be part of the sum.

ControlMapper::Axis wrote rawAxisValue_[axis.axisId] with no bounds check, one line
below an explicit check on axis.deviceId. axisId comes straight from the device -
Android reports AXIS_GENERIC_13..16 as 44..47, against a 44-entry array - so it wrote
into the neighbouring deviceTimestamps_. NativeAxis had the same unchecked write into
HLEPlugins::PluginDataAxis, where it goes out of the object entirely.

Rewind's LockedDecompress computed its copy-from-base block size as
base.size() - result.size() in size_t and truncated to int, so it went negative once
the output grew past the base, and insert() then ran with last < first. That happens
because a state can outlive the base it was compressed against: there are 20 states
but only 2 bases, rotated every 16 saves. Track a generation per base and refuse to
decode a state whose base is gone, and bound the block size against the base itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
2026-08-30 22:45:50 +02:00
Henrik RydgårdandClaude Opus 5 7a13881245 DepthRaster: fix host buffer overflows in the depth raster queue
CalculateDepthDraw guarded depthVertexCount_ with vertexCount, which is the
index count - but depthVertexCount_ grows by the number of decoded vertices,
which for an indexed draw can be far larger. Two draws with 6 indices spanning
40000 vertices each therefore passed the check and wrote ~700KB past the end of
depthTransformed_. It also never bounded depthIndexCount_ against depthIndices_
at all, which only has room for 3 indices per vertex slot while draws routinely
produce more. Pass the decoded count in separately and check both.

DepthRasterClipIndexedTriangles duplicated culling-disabled triangles twice: once
in the collect loop (added for Syphon Filter, #21498) and again in the output
stage, which was the older code and should have been removed then. So it emitted
four triangles per input triangle into buffers sized for one, and did twice the
rasterization work it needed to in that mode. Removed the output-stage copy, and
gave the function the output capacity so it stops when full - even at 2x, a
culling-disabled draw over ~32k indices doesn't fit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
2026-08-30 22:33:01 +02:00
Henrik Rydgård 4d26523ba8 Note the Kitten Cannon oversized-texture case
Homebrew "Kitten Cannon" hits the bad-dimensions path with a clearly invalid
512x32768 texture, likely a noise bit in the texture size command.
2026-08-29 11:42:47 +02:00
Henrik Rydgård 46d5fae342 VSH prep: Free up kernel memory by not storing the PPGe atlas in it 2026-08-19 18:45:52 +02:00
Henrik Rydgård 14338534e7 Improve some memory address checking functions 2026-08-19 18:40:01 +02:00
Henrik Rydgård 2096179dce Drive-by code cleanup 2026-08-15 18:31:20 +02:00
Henrik Rydgård c5e4d0d90d Rename the get-memory-pointer functions to make it clear where CPU exceptions can happen. 2026-08-12 14:06:16 +02:00
Henrik Rydgård 4d8a5d74e7 Merge pull request #22086 from hrydgard/read-u32-more
Some fixes to Claude's paranoia, more memory access function cleanup
2026-08-11 23:52:17 +02:00
Henrik Rydgård e062c90bf7 Try to fix the test difference (basically by replicating an old misfeature of headless...) 2026-08-11 22:36:47 +02:00
Henrik Rydgård 5198317e24 Back out some excessive checking in the latest changes. Change TOOD: to TODO: . 2026-08-11 22:27:44 +02:00
Henrik Rydgård 4f8859d0ec Centralize a disassembly utility function between the debuggers 2026-08-11 09:08:52 +02:00
Henrik RydgårdandClaude Sonnet 5 b322b0621c ReplacedTexture: fix stack OOB write from ZIM mip array contract
LoadZIMPtr() writes width[]/height[]/image[] as arrays (one entry per
mip level, up to ZIM_MAX_MIP_LEVELS) whenever the file has
ZIM_HAS_MIPS set, per its documented contract - but this caller passed
plain scalar locals. A texture-replacement .zim file with that flag
set caused multiple out-of-bounds stack writes. Now passes properly
sized arrays and only uses level 0, matching the existing "we don't
support ZIM mips yet" behavior. Also fixes a pre-existing leak of
image[0] on the "changed since header read" error path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4QAoxV2KY7ek4PcZw3WvY
2026-08-09 19:31:02 +02:00
Henrik Rydgård b6e7cb915a Texture cache management: include w and h in more log messages for context 2026-08-09 19:03:08 +02:00
Henrik Rydgård 459f4473af Fix crash in GE debugger
Fixes #22027
2026-08-07 23:49:30 +02:00
Henrik Rydgård 0324c7a912 Windows buildfix 2026-08-05 00:15:56 +02:00
Henrik Rydgård de7989ae56 Add TextureReplacer unit test; make replacer testable standalone
Add unittest/TestTextureReplacer which creates a fictive texture pack
(textures.ini with readable invented hashes plus real PNG files), loads
it via the replacer, and verifies lookups, filtering, hashranges, mip
levels, and missing/ignored entries.

To make the replacer runnable outside the emulator:
- The constructor now accepts a null DrawContext (formats just default
  to unsupported).
- FindReplacement/FindFiltering use the replaceEnabled_ member instead
  of the global config.
- Added TextureReplacer::LoadPackForTesting() to load an ini from a
  path directly.
2026-08-03 18:55:33 +02:00
Henrik Rydgård 0049b19fbf Fix OOB in texture replacer mip mixing and non-DXT texture decode
Vuln 17: ReplacedTexture::LoadLevelData let a KTX2/DDS file at a higher
mip level resize the shared data_ vector to its own (attacker-controlled)
mip count, so data_[mipLevel + i] indexed out of bounds and the KTX2
branch resized a different element than it wrote to. Disallow mixing
image formats across mip levels, cap the container mip count, and resize
the same element that is used as the transcode destination.

Vuln 18: DecodeTextureLevel only validated the start address for
non-DXT textures, so guest-controlled w/h/bufw could drive reads past
mapped RAM. Validate the needed range like the DXT path does and clamp
the height; ReadIndexedTex now takes the clamped w/h.
2026-08-01 13:16:22 +02:00
Henrik Rydgård 5e6a051952 Fix path traversal in texture pack filenames; use HasPathTraversal in savestate migration
Texture pack filenames/aliases from textures.ini were used to build read
and write paths with no '..' check, letting a malicious pack read or
write files outside the pack directory.

- LoadIniValues rejects entries with a parent dir component via
  HasParentDirComponent.
- ReplacedTexture::Prepare skips such filenames as defense in depth.
- PSPLoaders savestate migration now uses the shared HasPathTraversal
  helper instead of inline separator checks.
2026-08-01 13:16:22 +02:00
Henrik Rydgård 10a5d2f810 OpenGL: Improve checks for fragment shader float precision.
Fixes #22001 (although there may be other artifacts).
2026-07-30 10:03:15 +02:00
Henrik Rydgård 2a79257551 Merge pull request #21667 from dsprogrammingprojects/Texture-Replacement-Double-Wildcard
Texture replacement addition for Address Only case
2026-07-24 21:40:02 +02:00
Henrik Rydgård 240a974e95 Remove direct dependency from Core to MacCameraHelper.mm 2026-07-24 12:06:15 +02:00
Henrik Rydgård 986eee7ef3 Remove the global variable "useEmuThread", add some VK error checking for compute shader compiles 2026-07-18 23:45:27 +02:00
Henrik Rydgård 2931e9e5d2 TextureReplacer: We don't need to check for files that are missing from the alias list. 2026-07-18 18:00:54 +02:00
Henrik Rydgård 53c57bf95f Minor cleanups in replacer 2026-07-18 18:00:54 +02:00
Henrik Rydgård d7c8d6bd80 Clean up cachekey handling in texture replacer 2026-07-18 18:00:51 +02:00
Henrik Rydgård 0b39cde3a3 LookupWildCard: Remove a redundant argument and a constant template argument 2026-07-18 17:55:04 +02:00
Henrik Rydgård 90d922226f Correct maxSeenV handling 2026-07-18 12:31:01 +02:00
Henrik Rydgård 63331fd6d1 Remove DeleteTexture function, only made the code less clear 2026-07-18 12:08:50 +02:00
Henrik Rydgård ee1314f803 Add new TexCache logging channel 2026-07-18 11:57:56 +02:00
Henrik Rydgård f3a5b2dd18 Correct bugs in the secondary texture cache 2026-07-18 11:57:48 +02:00
Henrik Rydgård cff09041c9 Completely rework the flow in TextureCacheCommon::ApplyTexture 2026-07-18 11:57:48 +02:00
Henrik Rydgård b9b2b2b61e Reindent some code, do checks in a more logical order 2026-07-18 11:57:48 +02:00
Henrik Rydgård 1ffdc52111 Remove another "next" variable, cleanup 2026-07-18 11:57:48 +02:00
Henrik Rydgård 3b58242063 More cleanup 2026-07-16 20:54:58 +02:00
Henrik Rydgård ff89e4da4d Simplify 2026-07-16 20:38:27 +02:00
Henrik Rydgård 31c32c0ee0 Remove the "nextFramebufferTexture_" variable 2026-07-16 20:30:01 +02:00
Henrik Rydgård 40bce17f53 Some cleanup 2026-07-16 20:27:44 +02:00