From 886a2b249cf0dcbe75f954e89dbdaadea960e1ef Mon Sep 17 00:00:00 2001 From: orezraey Date: Fri, 23 Oct 2026 08:49:24 -0300 Subject: [PATCH] revert(libkernel): move the destroyed condition marker to its own pull request Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libkernel/Pthread/src/Cond.cpp | 4 +--- core/libs/tests/GuestPthreadDestroyedObjects.cpp | 9 --------- docs/dev/debt/libkernel.md | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/core/libs/prx/libkernel/Pthread/src/Cond.cpp b/core/libs/prx/libkernel/Pthread/src/Cond.cpp index 12ba2399d5..a3a3087bf7 100644 --- a/core/libs/prx/libkernel/Pthread/src/Cond.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Cond.cpp @@ -25,7 +25,7 @@ std::mutex& InitializationMutex() { } PthreadCond destroyedCond() { - return reinterpret_cast(std::uintptr_t{1}); + return reinterpret_cast(std::uintptr_t{2}); } PthreadMutex destroyedMutex() { @@ -175,8 +175,6 @@ int APS5_VABI scePthreadCondDestroy(PthreadCond* cond) { std::lock_guard lock(InitializationMutex()); if (*cond == destroyedCond()) return SCE_KERNEL_ERROR_EINVAL; - if (*cond == nullptr) - return 0; delete *cond; std::atomic_ref(*cond).store(destroyedCond(), std::memory_order_release); return 0; diff --git a/core/libs/tests/GuestPthreadDestroyedObjects.cpp b/core/libs/tests/GuestPthreadDestroyedObjects.cpp index 3ded0d5acf..ca35d7b099 100644 --- a/core/libs/tests/GuestPthreadDestroyedObjects.cpp +++ b/core/libs/tests/GuestPthreadDestroyedObjects.cpp @@ -53,19 +53,10 @@ int main() { PthreadCond cond = nullptr; Require(scePthreadCondInit(&cond, nullptr, "destroyed") == SCE_OK); Require(scePthreadCondDestroy(&cond) == SCE_OK); - Require(reinterpret_cast(cond) == 1); Require(scePthreadCondSignal(&cond) == SCE_KERNEL_ERROR_EINVAL); Require(scePthreadCondBroadcast(&cond) == SCE_KERNEL_ERROR_EINVAL); Require(scePthreadCondWait(&cond, &mutex) == SCE_KERNEL_ERROR_EINVAL); Require(pthread_cond_signal_nid_postfix(&cond) == POSIX_EINVAL); Require(scePthreadCondDestroy(&cond) == SCE_KERNEL_ERROR_EINVAL); Require(pthread_cond_destroy_nid_postfix(&cond) == POSIX_EINVAL); - - PthreadCond untouched = nullptr; - Require(scePthreadCondDestroy(&untouched) == SCE_OK && untouched == nullptr); - Require(scePthreadCondSignal(&untouched) == SCE_OK && untouched != nullptr); - Require(scePthreadCondDestroy(&untouched) == SCE_OK && reinterpret_cast(untouched) == 1); - untouched = nullptr; - Require(scePthreadCondBroadcast(&untouched) == SCE_OK && untouched != nullptr); - Require(scePthreadCondDestroy(&untouched) == SCE_OK); } diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index d459894f42..373170f18b 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -61,7 +61,7 @@ - [sceKernelSyncOnAddressWait](../../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored - [sceKernelSyncOnAddressWait8 / sceKernelSyncOnAddressWait16 / sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64](../../../core/libs/prx/libkernel/SyncOnAddress/src/SyncOnAddress.cpp) (libkernel) - libc's `__std_atomic_wait_direct_8/16/32/64` pass a microsecond timeout or null and ignore the result; the by-value comparand, return values and the 2- and 8-byte alignment of Wait16 and Wait64 are assumed - [scePthreadRwlockDestroy, pthread_rwlock_destroy](../../../core/libs/prx/libkernel/Pthread/src/Rwlock.cpp) (libkernel) - measured on a PS5 (firmware 13.60): destroying a rwlock returns 0 whether it is free or held for reading or writing, and leaves 1 in the handle; every call on that handle then returns `EINVAL` until it is initialized again, and a zero handle is initialized on first use by the `scePthreadRwlock` calls as by the POSIX ones. A rwlock destroyed while held is not freed, so its memory leaks. Destroying a rwlock while another thread is acquiring it is not guarded -- [scePthreadCondWait / scePthreadCondTimedwait](../../../core/libs/prx/libkernel/Pthread/src/Cond.cpp) (libkernel) - the mutex's owner is checked before waiting, as in FreeBSD libthr's [`cond_wait_common`](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_cond.c#L1992-L2029). Measured in a payload process on a PS5 (firmware 13.60): `EPERM` (`0x80020001`) for an unowned mutex of every type, the adaptive initializer (slot 1), an uninitialized slot and a mutex owned by another thread, `EINVAL` for the destroyed mutex (slot 2), and a recursive mutex is released completely and relocked to its depth. `scePthreadCondDestroy` stores 1, which every condition call refuses with `EINVAL`, and leaves an uninitialized slot alone. Cancellation restores the mutex and depth before cleanup and a canceled waiter forwards a wake, per POSIX; its ordering is not measured +- [scePthreadCondWait / scePthreadCondTimedwait](../../../core/libs/prx/libkernel/Pthread/src/Cond.cpp) (libkernel) - before waiting, FreeBSD libthr's [`cond_wait_common`](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_cond.c#L1992-L2029) checks ownership through [`_mutex_owned`](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_mutex.c#L1166-L1178): an unowned mutex returns `EPERM`, while its destroyed marker returns `EINVAL`. An untouched adaptive static initializer (slot value 1) therefore returns `EPERM`; the test covers this, an uninitialized slot, a mutex owned by another thread, and the destroyed marker for both timed and untimed sce and POSIX waits. A recursive mutex locked several times is released completely for the wait and relocked to the same depth afterwards, as FreeBSD libthr does (`_mutex_cv_unlock`, `_mutex_cv_lock`). These FreeBSD-derived behaviors remain unverified on console hardware. Cancellation follows the [POSIX condition wait contract](https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_cond_clockwait.html): the original mutex and recursive depth are restored before cleanup, and a canceled waiter forwards a wake to the remaining waiters. The host condition state is retained across mutex reacquisition so the guest can destroy and free its condition after notification; cancellation ordering and signal replacement are not measured on a console - [sceKernelSetEventFlag](../../../core/libs/prx/libkernel/EventFlag/src/EventFlag.cpp) (libkernel) - multi-waiter satisfaction follows fpPS4 in-order evaluation: registered waiters are evaluated in FIFO order and each satisfied waiter's clear mode is applied immediately to the pattern before evaluating subsequent waiters; this ordering is unverified on PS5 console hardware - [sceKernelOpenEventFlag](../../../core/libs/prx/libkernel/EventFlag/src/EventFlag.cpp) (libkernel) - returns the handle of an undeleted event flag this process created with the same name, and `SCE_KERNEL_ERROR_ENOENT` otherwise. Flags owned by other processes, such as the shell's `SceShellCoreUtilAppFocus`, do not exist here, and which attribute makes a flag openable on the console is not modelled. The error code for a missing name and the choice between flags sharing a name are unverified. - [sceKernelAioSubmitReadCommandsMultiple / sceKernelAioSubmitWriteCommandsMultiple / sceKernelAioWaitRequests / sceKernelAioPollRequests / sceKernelAioCancelRequest / sceKernelAioCancelRequests / sceKernelAioDeleteRequests](../../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) - behaviour from shadPS4 (one id per request for the Multiple submits, cancel turns any request aborted and id 0 reports processing, wait mode 2 returns once one request completed, poll copies every state without waiting); the 128 requests or ids per batch limit is from SharpProspero, with an unknown error, so larger batches throw; wait modes other than 1 (and) and 2 (or) throw; null pointers, negative counts and invalid ids return the codes the single-request siblings use, checked before any state is written