Repository navigation
fix(libc): keep the unwinder's frame caches alive through static destruction - #16313
Merged
oneandonlydean merged 1 commit intoNov 6, 2026
Merged
oneandonlydean merged 1 commit into
oneandonlydean merged 1 commit into
Conversation
…ruction FindFrameFast (Linux) and IndexFrames (Windows) cache each image's data in a function-local static std::map guarded by a static std::mutex. Both are destroyed at exit when libc.prx is finalized, while another thread can still be throwing. Since boykopovar#14235 the run report reads the title metadata on a detached thread, and in the ASan nightly the throw from AppMetadata's copyToFixedBuffer in that thread reads the freed map (heap-use-after-free at Unwind.cpp:233) in download_data_mount_invalid, guest_flexible_memory_invalid and three guest_convert_keycode_title tests. Allocate the caches and their mutexes and never destroy them, as libkernel does for its unwind images, its frame table cache and its thread stacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Windows check of head Setup: Windows 11 Enterprise 22621, WinLibs GCC 15.2 UCRT, Release. This is not an ASan build: MinGW has no AddressSanitizer, so the heap-use-after-free itself isn't observable here. The hosted Windows check does not run these tests on pull requests.
So keeping the frame caches alive through static destruction doesn't regress the unwinder on Windows. AI-assisted: yes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The nightly Sanitizers job of 11-06 (42158260477, head ea3ae18) fails five tests with
AddressSanitizer: heap-use-after-freeatcore/libs/prx/libc/src/exception/Unwind.cpp:233:download_data_mount_invalidguest_flexible_memory_invalidguest_convert_keycode_title_malformed-jsonguest_convert_keycode_title_oversized-idguest_convert_keycode_title_oversized-titleNone of them failed in the 11-05 nightly (head 1882108).
FindFrameFast(Linux, glibc 2.35 and later) caches each image's text and data bases in a function-localstatic std::mapguarded by astatic std::mutex. Function-local statics are destroyed at exit. In the report, the map is freed on the main thread by__cxa_finalizewhen_dl_finifinalizeslibc.prx, and a thread that is still throwing then reads it.#14235 (merged 11-05) added that thread.
RunReport::Collector::RefreshGamereads the title metadata on a detached thread. In these tests the metadata is invalid, socopyToFixedBufferinAppMetadata.cppthrows there while the process exits:The run report only exposes the bug. Any guest thread that throws while the title exits can read the freed map.
IndexFrames, the Windows path, has the same pattern: a static map and mutex, plus a returned reference into the map that is used after the lock is released.This PR allocates both caches and their mutexes and never destroys them. libkernel already does this for its unwind images (
Module.cpp:42), its frame table cache (ModuleInfo.cpp:259) and its thread stacks and their lock (#9272, #9960). Changes one file,core/libs/prx/libc/src/exception/Unwind.cpp(4 lines).Tested
Ubuntu 24.04, GCC 13.3,
mainbbd85f7, configured with the Sanitizers workflow's flags (Release,-DANYPS5_ENABLE_ASAN=ON,-g1 -fsanitize=undefined -fno-sanitize=vptr), run with its environment (ASAN_OPTIONS=detect_leaks=0,UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1). I ran every test of the three executables, 12 tests in all:guest_convert_keycode_title_*download_data_mount_*guest_flexible_memory_invalidResults:
main(red): the same five tests fail in 3 of 3 runs. Each prints the use-after-free above: read atUnwind.cpp:233, allocated atUnwind.cpp:245, freed by__cxa_finalizeinlibc.prx's fini, on theRunReport.cpp:1310thread.exception_runtime,exception_personality,exception_compact_unwind,exception_c_cleanup,unwind_callback_abi,unwind_record_bounds,guest_unwind_module_info(_failure),guest_raise_exception,guest_exception_handler,guest_sce_v2_exceptions,guest_libc_internal_exception_destructors,guest_throw_bad_array_new_length,guest_cxa_guardandguest_json_initialization_exceptions.python3 -I tools/check_conventions.py --base origin/mainpasses andgit diff --checkis clean.Not run:
IndexFramesis only compiled for_WIN32and I have no Windows toolchain, so CI is its first compile.Overlap
I checked the file lists of all 1884 open PRs (2026-11-06). 13 of them list more than 100 files and were truncated; I didn't check those further. Seven change
Unwind.cpp:git merge-treeof each with this branch is clean.baseson the same declaration line (std::pair<Word, Word>→ObjectBases), so it gets a one-line conflict. The resolution isstatic auto& bases = *new std::map<std::tuple<const Byte*, const void*, const void*>, ObjectBases>();. feat(libc): answer every libunwind cursor slot of an unwind context #6819 already conflicts withmainindocs/dev/debt/libc.mdand is labelled "needs rebase".No open PR changes the run report's detached thread for this. #15973 keeps
.detach().Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code