Repository navigation
fix(libkernel): return ESRCH from scePthreadAttrGet for a thread that has ended - #7957
Merged
oneandonlydean merged 1 commit intoOct 20, 2026
Conversation
… has ended scePthreadAttrGet copied the attributes of any non-null handle, so a thread that was joined or had finished still answered 0, and a thread that ended detached was read after its object had been freed. FreeBSD 11.4's _pthread_attr_get_np returns ESRCH for a dead or reclaimed thread, and Monster Hunter Wilds (PPSA24191) reuses a slot of its thread table only when scePthreadAttrGet returns 0x80020003. After the EINVAL checks it now returns ESRCH unless the handle is a live guest thread, and copies the fields while holding the thread registry lock (LockLiveGuestThread), so a detached thread exiting during the call is not freed under the copy. GuestThreadIsLive uses the same lookup. pthread_attr_get_np, which the title does not import, is left as it is; the debt entry records the difference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
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
On
main,scePthreadAttrGetchecks only for null arguments and then copies the attributes of whatever the handle points at. A thread that has ended still answers 0: one that was joined, one that finished and is not joined yet, and one that ended detached. For the last kindReleaseThreaddeleted the object when the thread ended, so the call reads freed memory. A probe run againstlibkernel.prxbuilt frommain5eba12d printed the following (cut to thescePthreadAttrGetresults; the last line comes from a copy of the slot search described below, run by a 65th thread after 64 others had registered and been joined):FreeBSD 11.4 answers
ESRCHin all three cases._pthread_attr_get_npreturnsEINVALfor a null thread or attribute, then looks the thread up with_thr_find_thread(curthread, pthread, /*include dead*/0)._thr_find_threadreturnsESRCHfor a thread that is no longer in the thread hash or is inPS_DEAD, and otherwise returns with the thread locked; the attributes are read before the unlock.exit_threadsetsPS_DEADafter the thread-specific data destructors have run (thr_exit.c#L287-L305). Where the console was measured (firmware 13.60) it answers ended threads the same way: a secondscePthreadJoinreturns0x80020003(measured in #4160, implemented in #4481) andpthread_getname_npon a joined thread returnsESRCH(recorded in its debt entry, implemented in #4533).Monster Hunter Wilds (PPSA24191) depends on this answer. It gives each thread an index from a table of 64 slots. A thread that is not in the table yet walks the slots in order and takes the first one that is empty or whose thread
scePthreadAttrGetreports gone: for each filled slot it passes, it callsscePthreadAttrInit,scePthreadAttrGeton the slot's thread andscePthreadAttrDestroy, and takes the slot only ifscePthreadAttrGetreturned0x80020003. Withmain's answer no slot is ever freed, so once 64 distinct threads have taken one, later threads get none unless their handle happens to equal an old one. No stop has been observed: here the title does not get past a flat grey screen yet (it needs #7852 and--linux-fixed-baseto get that far), and none of the 7 runs logged so far started more than 50 guest threads.#5584 asked for the same check in
pthread_attr_get_np(withpthread_getaffinity_np) and was closed because no title was known to reach it. Monster Hunter Wilds does not importpthread_attr_get_np, so this PR leaves it as it is and changes onlyscePthreadAttrGet, the call the title makes. The difference from #5584 is a title whose code callsscePthreadAttrGetand acts on0x80020003; there is still no stop or log line.The change: after the existing
EINVALchecks,scePthreadAttrGetreturnsESRCH(0x80020003) unless the handle is a live guest thread in the thread registry, the testpthread_setaffinity_npandpthread_killalready make throughGuestThreadIsLive. The lookup compares pointers, so a freed handle is not read. The newLockLiveGuestThreadreturns the registry lock held when the thread is live, and the attributes are copied under it. A thread's object leaves the registry in its destructor under the same lock, so a detached thread that exits during the call is not freed while its fields are read.GuestThreadIsLivenow callsLockLiveGuestThread. A thread counts as ended once its thread-specific data destructors have run, where FreeBSD setsPS_DEAD, so a thread that queries itself from one of its destructors still gets its attributes. Running threads, including the main thread and other adopted host threads, are in the registry and answer as before.pthread_attr_get_npand the other calls that take a thread handle are unchanged.Not measured on a console. The new libkernel debt entry records that, that
pthread_attr_get_npstill answers 0 for an ended thread, and that on Windows a host thread adopted byscePthreadSelfthat ends withoutscePthreadExitkeeps answering 0, since its object is never marked ended or freed.Tested
Gentoo Linux, kernel 7.2.8, GCC 16.2.1, glibc 2.43, Ninja, Release,
BUILD_TESTING=ON, onmain43a2aba.guest_pthread_attr(extended):scePthreadAttrGetreturns 0 for the main thread querying itself and for a thread querying itself from a thread-specific data destructor as it ends. It returns0x80020003for a joined thread, for a thread that finished and is not joined yet (polled for up to 5 s until the answer changes) and for the same thread after the join, and for 32 detached threads that ended. A null or uninitialized attribute still returnsEINVALfor an ended thread, and a failed call leaves the stack size already in the attribute. The test passed 100 runs in a row and 3 × 40 runs in parallel, about 40 ms per run.Negative control: with
Thread.cpp,Pthread.hppandAttr.cpptaken frommainand the new test kept,guest_pthread_attraborts at its first joined-thread assertion (scePthreadAttrGetreturned 0; the line was found with a-gbuild of the test under gdb). The probe above, run against this branch'slibkernel.prx, prints0x80020003for the three ended threads, 0 for the calling thread, and slot 0 for thread fix(agc): keep BGRA color targets resident between draws #65.Tests the change reaches, selected by
tools/select_tests.pyas CI selects them (1349 of 1638): 1334 passed, 0 failed, 15 skipped. The skipped ones are the 13video_out_flip_*tests,video_out_frame_dump_queueandagc_driver_stencil_ref_export, which skip on this machine onmainas well.Windows:
x86_64-w64-mingw32-g++16.2.0-fsyntax-only -Wall -Wextraon the three changed.cppfiles: no warnings, no errors. This is a syntax check, not a Windows build or run.tools/check_compile.py --base upstream/main: the 3 changed translation units compile.tools/check_conventions.pypasses.Open PRs on the same code: fix(libkernel): report thread guard size from attribute queries #6893 (guard size) also changes
scePthreadAttrGetand adds its debt entry afterpthread_setaffinity_np; this branch puts its entry elsewhere,git merge-treemerges the two branches without conflicts, and neither depends on the other. fix(threads): run Windows pthread and small fiber stacks in guest VA range #7149 adds a Windows test that requiresscePthreadAttrGetto return 0 right after it creates threads whose entry callsscePthreadExitat once; with this change that call returns0x80020003when the thread has already ended, so whichever of the two is merged second has to query from inside the thread or keep it running until the query is done.tools/pr_overlap.py --branchreports no overlapping lines. It could not compare the open PRs that conflict withmain; merged with this branch, fix(libkernel): keep the signal mask per thread #3986, feat(aot): consolidate native ARM64 compiler, runtime and consumers #5021, feat(wasm): run relinked titles in the browser with WebGPU #6879, fix(threads): run Windows pthread and small fiber stacks in guest VA range #7149 and Macos port #7201 conflict in the same files as they do withmain, and fix(libkernel): report thread guard size from attribute queries #6893 and feat: macOS on Apple Silicon host support (Rosetta 2 and MoltenVK) #7332 merge cleanly.Monster Hunter Wilds (PPSA24191, 01.041.040, relinked with
--linux-fixed-base 0x400000), 6 minutes headless with gdb breakpoints where its table routine fills a slot and where it gives up with -1: with the libraries ofmain83bf557 plus feat(libs): add the Voice and IME exports PPSA24191 needs to load #7852, 44 of the 64 slots were filled and none was given up; with this branch (on 5eba12d) merged with feat(libs): add the Voice and IME exports PPSA24191 needs to load #7852, again 44 slots and no -1. No slot came up for reuse in that time, so neither run reaches the changed answer; they show how full the table gets before the grey screen.Not run on Windows, macOS or a console.
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