Repository navigation
fix(libkernel): fail scePthreadMutexUnlock on a statically initialized mutex with EPERM - #8617
Conversation
…d mutex with EPERM scePthreadMutexUnlock resolved the slot with resolveMutex(mutex, false), which throws "Mutex is not initialized" while the slot still holds the static initializer (0) or the adaptive static initializer (1), so unlocking a statically initialized mutex that was never locked ended the process. FreeBSD 11.4's mutex_unlock_common returns EPERM for both without initializing the slot, and EINVAL for the destroyed marker. Load the slot once: the destroyed marker still returns 0x80020016, and the two initializers now return 0x80020001 and stay in the slot, so a later lock still creates the mutex. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Independent Windows run of this PR (data only). Environment: Windows 10 Pro 10.0.19045, Ryzen 7 5800X3D, WinLibs GCC 15.2.0 r7 (MinGW-w64 UCRT), Release, BUILD_TESTING=ON. Merge-tree of the PR head against Results (3 runs each):
Not run: other platforms, the other pthread tests that call AI-assisted (Claude Code). |
|
Windows run of c68ffcf merged onto
|
|
Checked the overlap note by hand. #8373's only change to AI-assisted: yes (Claude Code). |
|
A related console data point, not the same case: measured on a PS4 Pro (firmware 10.71, So an initialised mutex the caller does not hold, normal or recursive, answers |
|
Follow-up: I have now measured the static-initializer case itself on the PS4 Pro (firmware 10.71), same homebrew probe. Each slot is a plain 8-byte variable set to 0 or 1 before the call; So the console answers exactly as this PR does: unlocking a slot that still holds 0 or 1 returns |
What
On
main,scePthreadMutexUnlockresolves the slot withresolveMutex(mutex, false), which throws while the slot still holds the static initializer (0) or the adaptive static initializer (1). Unlocking a statically initialized mutex that was never locked ends the process:No title is known to reach this.
FreeBSD 11.4's
_pthread_mutex_unlockpasses the slot value tomutex_unlock_commonwithout initializing it, andmutex_unlock_commonreturnsEINVALfor the destroyed marker (2) andEPERMfor the two initializers (NULLand 1,thr_private.h). #6244 added theEINVALhalf. shadPS4 registersscePthreadMutexUnlock(tn3VlD0hG60) asORBIS(posix_pthread_mutex_unlock), which has the same guard. Herepthread_mutex_unlockandscePthreadCondWaitalready answerEPERMfor these slots.The change:
scePthreadMutexUnlockloads the slot once without initializing it, asmutexOwnershipinCond.cppdoes. The destroyed marker still returns0x80020016. The two initializers return0x80020001and stay in the slot, so a later lock still creates the mutex (an adaptive one for 1). A null mutex pointer still throws, and a live mutex answers as before.resolveMutex(..., false)has no caller left; the parameter stays because open #8400 addsresolveMutex(mutex, true)callers.Not measured on a console.
Tested
Gentoo Linux, kernel 7.2.8, GCC 16.2.1, glibc 2.43, Ninja, Release,
BUILD_TESTING=ON, onmain989b6ea; after rebasing ontomainb85d972,guest_pthread_destroyed_objects,guest_pthread_mutex_errorsandguest_pthread_mutexattrpass again (5 of 5 runs).guest_pthread_destroyed_objects(extended):scePthreadMutexUnlockon a slot holding 0 returns0x80020001and leaves 0; the slot then locks and unlocks (0), a second unlock returns0x80020001and destroy returns 0. On a slot holding 1 it returns0x80020001and leaves 1; lock, unlock and destroy then return 0. Passed 20 runs in a row.Mutex.cppfrommainand the new test kept, the test aborts at its first new assertion with theMutex is not initializedlines above. A probe linked against thatlibkernel.prxshows that the slot holding 1 throws the same way. Against this branch both slots return0x80020001and stay unchanged, and the destroyed slot (0x80020016), an unlocked mutex and one held by another thread (0x80020001) and one held by the caller (0) answer as onmain.tools/select_tests.pyas CI selects them (1416 of 1716): 1400 passed, 0 failed, 16 skipped. The skipped ones are the 13video_out_flip_*tests,video_out_frame_dump_queueandvideo_out_telemetry_caption(no display or Vulkan device under SDL's offscreen driver) andagc_driver_stencil_ref_export(the device lacksVK_EXT_shader_stencil_export). They skip on this machine onmaintoo;video_out_telemetry_captionwas checked withmain'sMutex.cpp.x86_64-w64-mingw32-g++16.2.0-fsyntax-only -Wall -Wextraon the two 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 2 changed translation units compile.tools/check_conventions.pypasses.tools/pr_overlap.py --branch fix/libkernel-mutex-unlock-static-eperm: no overlap with open pull requests.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