Skip to content

fix(libSceNgs2.native): keep the system list and its mutex alive through static teardown - #1197

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
gookie-dev:fix/ngs2-state-outlives-exit
Oct 7, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
gookie-dev:fix/ngs2-state-outlives-exit

Conversation

@gookie-dev

Copy link
Copy Markdown
Contributor

What

NGS2 looks up every system, rack and voice handle in a function-local std::vector guarded by a function-local std::recursive_mutex (System.cpp). Both were destroyed with the other statics when the process exits. std::exit runs the destructors on the exiting thread while the title's other threads keep running, so an audio thread that renders during exit finds the freed vector:

  • sceNgs2SystemRender returns SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE (the freed vector's first element now holds the allocator's free-list pointer), and
  • a lookup that walks the racks (Ngs2FindVoice from sceNgs2VoiceGetState) dereferences freed memory and segfaults.

Both objects are now allocated once and never destroyed, as other driver state that must outlive static teardown already is (Draw.cpp, Pipeline.cpp, Recorder.cpp).

Tested

Pop!_OS 24.04 (kernel 7.1.5), GCC 13.3.0, CMake 3.28.3, Ninja, Release, BUILD_TESTING=ON, SPIRV-Tools validation off, NVIDIA GeForce RTX 4050 Laptop GPU.

Checklist

…ugh static teardown

sceNgs2SystemRender and the other entry points look up their handles in a
function-local std::vector guarded by a function-local std::recursive_mutex.
Both were destroyed with the other statics when the process exits, while a
title's audio thread can still be rendering: std::exit runs the destructors
on the exiting thread and other threads keep running until the process
ends. The freed vector then no longer holds the system, so rendering
returns SCE_NGS2_ERROR_INVALID_SYSTEM_HANDLE, and a lookup that walks the
racks reads freed memory and crashes.

Both are now allocated once and never destroyed, as other driver state that
must outlive static teardown already is.

PPSA21402 (Balatro) printed "Error: sceNgs2SystemRender() 804A0230" and
segfaulted in Ngs2FindVoice (from sceNgs2VoiceGetState) when it was closed.

ngs2_render now renders from an atexit handler registered before the first
NGS2 call, which runs after the library's statics are destroyed; without
this change it fails on that render.
@oneandonlydean
oneandonlydean merged commit 91b6a46 into boykopovar:main Oct 7, 2026
4 checks passed
@gookie-dev
gookie-dev deleted the fix/ngs2-state-outlives-exit branch October 11, 2026 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants