Repository navigation
Description
SONIC SUPERSTARS (PPSA06888) crashes in its own memory allocator after a few deaths and respawns in a stage. The game's heap metadata is overwritten with what looks like texel data. AnyPS5 logs no error before it. One earlier run froze at the same point instead: the game's render thread stopped calling sce::Agc::suspendPoint for 52 s, with no AnyPS5 error and no WAIT_REG_MEM timeout. That may be the same corruption hitting the allocator another way.
The crash (Windows minidump of the relinked app.exe):
EXCEPTION_ACCESS_VIOLATION (non-canonical address) at app.exe+0x1697ee9, in the guest eboot code
141697edb mov 0x10(%r8),%rsi
141697edf mov 0x18(%r8),%rdx
141697ee9 mov %rdx,0x18(%rsi) <- faults
141697eed mov %rsi,0x10(%rdx)
r8 = 0x28e18fff0 rsi = rdx = 0x00ffffff00ffffff rdi = 0x200040890
The surrounding code (lzcnt, size classes, cmp %r8,0x88(%rdx,%r10,8)) is a TLSF-style allocator unlinking a free block. The block header is at 0x28e18fff0, so its free-list pointers at +0x10 and +0x18 are the first 16 bytes at 0x28e190000, and they hold 0x00ffffff dwords.
The last AnyPS5 log lines before the crash create a storage image at exactly that address:
StorageTexture address=0x28e190000 32x32 mips=1 mip=0 layers=0 base=0 dim=1 tile=7 format=56 vk=37
Texture address=0x28e190000 32x32 mips=1 viewed from storage image (vk=37)
So the game freed that block and the first texels of a 32x32 RGBA8 storage image (0x00ffffff: white, alpha 0) were stored over its free-list pointers. Either the game's GPU work on the console wrote them before the free, while on AnyPS5 the result reaches guest memory after it, or something wrote them over memory that was already free.
What I checked: a log in StorageTexture::writeBackLayers over a later run (306 write-backs). Every write-back in this title is untracked (tracked=0), and every stored layer was compared with its saved original first (compared equals stored, unsavedOutsideWatch=0). So the deferred write-back skips 64 KiB blocks the CPU changed before the comparison, and I could not confirm it as the writer. That run hit the multisampled-memory stop (#5933) before the crash, so it did not include the crash itself. Not checked: writes the CPU makes after the comparison and before the store, shader stores through BDA, and DMA destinations.
Write watching: GuestMemory::Watched was false for every multisampled target checked in these runs (see #5933), so CPU stores into that memory are not tracked. Whether the storage image's memory at 0x28e190000 is watched was not checked.
Setup: Windows 11, MinGW-w64 GCC 15, Release, AMD Radeon RX 5700 XT. main 5a6ad0ed3 plus #5865, #5375, #5456, #5882, #4820 and #5445, and a local footprint for the Z64KBX layout. The dump and logs are available on request.
AI-assisted (Claude Code).
Activity
romainveurrier commentedon Oct 14, 2026
Hi!
I've reproduced a compare-to-publication ordering window in a controlled synthetic test on NVIDIA (RTX 2050, driver 615.71.09) under Fedora 44.
CPU changes made after the storage-image keep-range selection can be overwritten by subsequent publication. However, the experiment deliberately forces that ordering, and I haven't established that such CPU modifications are valid under the guest synchronization contract.
Would you be able to share the exact commit SHAs for your reproduction patch stack, your local Z64KBX layout changes, the AMD driver version, and the reproducible death/respawn sequence?
Sanitized logs showing the events around the corrupted address would also be very useful. If sharing minidump-derived information is possible, please let me know what you can safely provide.
billythemenace2 commentedon Oct 14, 2026
@romainveurrier Thanks for looking into this. Here is what I can give.
Exact build: the crash came from this tree, now pushed as
billythemenace2/AnyPS5@fb4b8d650(branchtest/sonic-superstars-5934). It starts frommain5a6ad0ed3, as the issue says, but merging #4820 also brought inmainup to066b58fa1. On top of that it has:3c81a744b, fix(shader): read 1D T#s through 3D image_sample and image_load accesses #5375 at0bcc6b4e1with its disk cache field count in82c4530ae, fix(shader): compare through 2D image_sample_c on 1D T#s #5456 at7bce94920, and fix(agc): rasterize a multisampled target without MSAA_ENABLE at the pixel center #5882 ascd16c94e9plusaf5f99da3(the sameState.cppchange as its pushedd9d8fb737)84f636494, and fix(agc): sample a multisampled texture no draw rendered to from its uniform color memory #5445 at556890fb3, with a merge fix in1aadef9ff(fix(agc): sample a multisampled texture no draw rendered to from its uniform color memory #5445's fill path passing the target'sactiveflag toguard)445fc79b5:createFilledmapsTextureTileMode::kZ64KBXtoColorTileMode::Z64KBX, andMultisampledColorBytesgivesZ64KBXthe same 64 KiB block rule asStandard64KBandRenderTarget41b6be159, a local retire-on-CPU-store change for multisampled targets. It never fired in these runs, because the targets' memory is not write-watched (AGC: multisampled targets whose memory a title reuses throw on the next access (SONIC SUPERSTARS PPSA06888) #5933).70c29a7e3and its revertfb4b8d650cancel out. No diagnostic logging was in this build.Driver: AMD Radeon RX 5700 XT, AMD driver 32.0.21045.5002 (17 August 2026), Windows 11 26200. The app selects the 5700 XT over the Ryzen iGPU.
Sequence: dying and respawning three times in the last act of the first zone, not in a bonus stage. The crash came on the third respawn. An earlier run on a nearly identical tree froze at a respawn instead. The run that hit the reuse stop (#5933) stopped before any respawn crash.
Logs: the stdout and stderr of the crash run were overwritten by later runs, so only the minidump remains. Its last AnyPS5 lines before the crash, quoted in the issue, came from the live log of that run. I won't attach the minidump itself, since it contains the game's memory. I can extract more from it: other threads' stacks, registers, or which memory ranges the dump holds around
0x28e190000.AI-assisted (Claude Code).
romainveurrier commentedon Oct 16, 2026
Thanks for the build details. Synthetic tests on your branch reproduce two ways the deferred storage-image write-back stores the old texels over links the title wrote after the dispatch's label. Both leave
0x00ffffff00ffffffat +0x0 and +0x8, as in your dump:Both reproduce on NVIDIA and Intel and are fixed in #6960 . I haven't confirmed in-game that one of them is your crash. Could you try the PR branch on your 5700 XT?
APS5_DEFER_COMPARED_RESULTS=1, which restores the old deferral. If the crash returns, that points to case 1.billythemenace2 commentedon Oct 17, 2026
@romainveurrier thank you, both cases fit what I saw, and the repeated-respawn timing especially fits case 1.
I can't run the respawn sequence on #6960 yet. On current
mainplus the PRs the title needs (#6297, #6300 and #6808), SONIC SUPERSTARS now stops a few seconds in, atsample-preserving guest publication of native multisample depth is not implemented(details on #6808). That's before the first zone. The 14 October tree that reached the stages predates too much: #6960'sTexture.cppchanges don't apply to it without a manual port, and a port would no longer be testing your code as written.Once the depth publication work (Amoenus's stack ending in #7002) gets the title back into the stages on
main, I'll run your three steps: the three deaths in the last act, thenAPS5_DEFER_COMPARED_RESULTS=1if it no longer crashes, and the frame-rate comparison. I'll post the results here.AI-assisted (Claude Code).
billythemenace2 commentedon Oct 17, 2026
@romainveurrier I ran #6960 on the 5700 XT. The result is partial: neither run reached three deaths, but the frame-rate difference is clear.
Build. Current
mainstill can't reach the stages. With #6808, Amoenus's stack ending in #7002, #6727 and #6960 merged, the title stops a few seconds in atsample-preserving guest publication of native multisample depth is not implemented. #6727 doesn't cover it because the game draws into that surface. So I ported #6960 onto the 14 October tree (fb4b8d650, the one in my earlier comment). The non-test files applied as written except twoTexture.cpphunks: that tree has oneHostImportForimport instead ofimportSegments(). SowriteBackLayerstakesconst HostImport* importunder yourrecordedWriteBack || pendingSinceCompletion == guestCompletionscondition, readsoriginalwhen it's null, and stores through the import when it isn't.StoreBeforeGuestCompletion, bothPacketMemory.cppandVulkanDevice.cppcall sites, andWriteChangedOverOriginal(Committed)are unchanged. This tests a port, not your branch as written.Run 1, #6960 default. It crashed in the first zone before any death, at the multisampled reuse stop from #5933:
a texture read of 0x29ddc1000+0x20000 overlaps multisampled color target 0x29dde0000+0x10000 (4 samples). Nothe CPU stored into ... during its write-backline was logged.Run 2,
APS5_DEFER_COMPARED_RESULTS=1. One death and respawn without a crash, then the act finished. It froze on the level-complete screen with no AnyPS5 error and no GPU timeout, and the frame counter stopped. A gdb dump shows every thread waiting, none inside a driver lock. The 14 October build froze the same way at a respawn. One respawn proves nothing about case 1 or case 2.Frame rate. The title's FPS counter, sampled every 15 s in the same stretch of the first zone:
APS5_DEFER_COMPARED_RESULTS=1So on this title the store at each label/interrupt roughly halves the frame rate. I didn't collect a Performance report form trace. I can if you tell me which switches you want.
AI-assisted (Claude Code).