Files
AnyPS5/core/libs/tests/GuestPthreadDestroyedObjects.cpp
T
Lindamea1andClaude Opus 5.5 c68ffcfd21 fix(libkernel): fail scePthreadMutexUnlock on a statically initialized 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 <[email protected]>
2026-10-20 19:31:14 +01:00

63 lines
3.1 KiB
C++

#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>
extern "C" {
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name);
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex);
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
int APS5_VABI scePthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char* name);
int APS5_VABI scePthreadCondDestroy(PthreadCond* cond);
int APS5_VABI scePthreadCondSignal(PthreadCond* cond);
int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond);
int APS5_VABI scePthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
int APS5_VABI pthread_mutex_lock_nid_postfix(PthreadMutex* mutex);
int APS5_VABI pthread_cond_signal_nid_postfix(PthreadCond* cond);
int APS5_VABI pthread_cond_destroy_nid_postfix(PthreadCond* cond);
}
static constexpr int SCE_OK = 0;
static constexpr int SCE_KERNEL_ERROR_EPERM = static_cast<int>(0x80020001u);
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016u);
static constexpr int POSIX_EINVAL = 22;
static void Require(bool value) { if (!value) std::abort(); }
int main() {
PthreadMutex mutex = nullptr;
Require(scePthreadMutexInit(&mutex, nullptr, "destroyed") == SCE_OK);
Require(scePthreadMutexDestroy(&mutex) == SCE_OK);
Require(scePthreadMutexLock(&mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadMutexTrylock(&mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(scePthreadMutexUnlock(&mutex) == SCE_KERNEL_ERROR_EINVAL);
Require(pthread_mutex_lock_nid_postfix(&mutex) == POSIX_EINVAL);
Require(mutex == reinterpret_cast<PthreadMutex>(std::uintptr_t{2}));
PthreadMutex uninitialized = nullptr;
Require(scePthreadMutexUnlock(&uninitialized) == SCE_KERNEL_ERROR_EPERM);
Require(uninitialized == nullptr);
Require(scePthreadMutexLock(&uninitialized) == SCE_OK);
Require(scePthreadMutexUnlock(&uninitialized) == SCE_OK);
Require(scePthreadMutexUnlock(&uninitialized) == SCE_KERNEL_ERROR_EPERM);
Require(scePthreadMutexDestroy(&uninitialized) == SCE_OK);
PthreadMutex adaptive = reinterpret_cast<PthreadMutex>(std::uintptr_t{1});
Require(scePthreadMutexUnlock(&adaptive) == SCE_KERNEL_ERROR_EPERM);
Require(adaptive == reinterpret_cast<PthreadMutex>(std::uintptr_t{1}));
Require(scePthreadMutexLock(&adaptive) == SCE_OK);
Require(scePthreadMutexUnlock(&adaptive) == SCE_OK);
Require(scePthreadMutexDestroy(&adaptive) == SCE_OK);
PthreadCond cond = nullptr;
Require(scePthreadCondInit(&cond, nullptr, "destroyed") == SCE_OK);
Require(scePthreadCondDestroy(&cond) == SCE_OK);
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);
}