diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 50defed7ecf2..1da98ed1ee36 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -518,6 +518,16 @@ if(BUILD_TESTING) set_tests_properties(guest_pthread_mutexattr PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_pthread_destroyed_objects_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadDestroyedObjects.cpp) + target_include_directories(guest_pthread_destroyed_objects_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_pthread_destroyed_objects_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_pthread_destroyed_objects_tests) + add_test(NAME guest_pthread_destroyed_objects COMMAND guest_pthread_destroyed_objects_tests) + set_tests_properties(guest_pthread_destroyed_objects PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_pthread_destroyed_objects PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_pthread_mutex_errors_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadMutexErrors.cpp) target_include_directories(guest_pthread_mutex_errors_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_pthread_mutex_errors_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/Pthread/src/Cond.cpp b/core/libs/prx/libkernel/Pthread/src/Cond.cpp index 557a81aa3cd1..609acd98aad0 100644 --- a/core/libs/prx/libkernel/Pthread/src/Cond.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Cond.cpp @@ -40,7 +40,7 @@ std::shared_ptr resolveCond(PthreadCond* cond, int* clockI std::atomic_ref slot(*cond); auto current = slot.load(std::memory_order_acquire); if (current == destroyedCond()) - throw std::runtime_error("Condition variable has been destroyed"); + return nullptr; if (!current) { current = new PthreadCondPrivate(); slot.store(current, std::memory_order_release); @@ -84,6 +84,8 @@ int finishWait(std::shared_ptr& condition, bool timedOut) } int waitUntil(std::shared_ptr condition, PthreadMutex* mutex, std::optional deadlineNanos, const void* caller) { + if (!condition) + return SCE_KERNEL_ERROR_EINVAL; PthreadMutex m = nullptr; if (const int error = mutexOwnership(mutex, &m)) return error; @@ -124,6 +126,8 @@ int CondOperations::AbsoluteTimedwait(PthreadCond* cond, PthreadMutex* mutex, co KernelUseconds usec = 0; int clockId = 0; auto condition = resolveCond(cond, &clockId); + if (!condition) + return SCE_KERNEL_ERROR_EINVAL; if (!PosixThread::RelativeMicroseconds(clockId, abstime, &usec)) throw std::invalid_argument("Invalid absolute condition variable timeout"); return waitUntil(std::move(condition), mutex, TimedWait::DeadlineNanos(usec), __builtin_return_address(0)); @@ -167,25 +171,34 @@ int APS5_VABI scePthreadCondDestroy(PthreadCond* cond) { throw std::invalid_argument("Condition variable pointer is null"); std::lock_guard lock(condInitializationMutex); if (*cond == destroyedCond()) - throw std::runtime_error("Condition variable has already been destroyed"); + return SCE_KERNEL_ERROR_EINVAL; delete *cond; std::atomic_ref(*cond).store(destroyedCond(), std::memory_order_release); return 0; } int APS5_VABI scePthreadCondSignal(PthreadCond* cond) { - resolveCond(cond)->NotifyOne(); + const auto resolved = resolveCond(cond); + if (!resolved) + return SCE_KERNEL_ERROR_EINVAL; + resolved->NotifyOne(); return 0; } int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond) { - resolveCond(cond)->NotifyAll(); + const auto resolved = resolveCond(cond); + if (!resolved) + return SCE_KERNEL_ERROR_EINVAL; + resolved->NotifyAll(); return 0; } int APS5_VABI scePthreadCondSignalto(PthreadCond* cond, Pthread thread) { (void)thread; - resolveCond(cond)->NotifyAll(); + const auto resolved = resolveCond(cond); + if (!resolved) + return SCE_KERNEL_ERROR_EINVAL; + resolved->NotifyAll(); return 0; } diff --git a/core/libs/prx/libkernel/Pthread/src/Mutex.cpp b/core/libs/prx/libkernel/Pthread/src/Mutex.cpp index 93e0e693b6f2..876bd9ee2023 100644 --- a/core/libs/prx/libkernel/Pthread/src/Mutex.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Mutex.cpp @@ -39,7 +39,7 @@ PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) { std::lock_guard lock(initializationMutex); const auto current = slot.load(std::memory_order_acquire); if (current == destroyedMutex()) - throw std::runtime_error("Mutex has been destroyed"); + return nullptr; if (current && current != adaptiveInitializer()) return current; if (!initialize) @@ -53,6 +53,8 @@ PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) { template int acquireMutex(PthreadMutex mutex, TAcquire acquire, int unavailable, bool tryOnly) { + if (!mutex) + return SCE_KERNEL_ERROR_EINVAL; const auto thread = std::this_thread::get_id(); const bool owned = mutex->_owner.load(std::memory_order_acquire) == thread; if (owned && mutex->_type != MutexType::Recursive) { @@ -188,6 +190,8 @@ int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex) { int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex) { auto* current = resolveMutex(mutex, false); + if (!current) + return SCE_KERNEL_ERROR_EINVAL; if (current->_owner.load(std::memory_order_acquire) != std::this_thread::get_id()) return sceNotPermitted; bool released = true; diff --git a/core/libs/tests/GuestPthreadDestroyedObjects.cpp b/core/libs/tests/GuestPthreadDestroyedObjects.cpp new file mode 100644 index 000000000000..a9a041cfc022 --- /dev/null +++ b/core/libs/tests/GuestPthreadDestroyedObjects.cpp @@ -0,0 +1,46 @@ +#include "SceTypes.hpp" +#include +#include + +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_EINVAL = static_cast(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(std::uintptr_t{2})); + + 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); +}