mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
Merge pull request #6244 from xXPBSXx/fix/pthread-destroyed-einval
fix(libkernel): return EINVAL for pthread mutex and cond operations on a destroyed object
This commit is contained in:
4 files changed
+79
-6
No files matched your search
@@ -518,6 +518,16 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(guest_pthread_mutexattr PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
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:$<TARGET_FILE_DIR:libkernel>")
|
||||
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)
|
||||
|
||||
@@ -40,7 +40,7 @@ std::shared_ptr<TimedWait::Condition> resolveCond(PthreadCond* cond, int* clockI
|
||||
std::atomic_ref<PthreadCond> 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<TimedWait::Condition>& condition, bool timedOut)
|
||||
}
|
||||
|
||||
int waitUntil(std::shared_ptr<TimedWait::Condition> condition, PthreadMutex* mutex, std::optional<std::uint64_t> 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<PthreadCond>(*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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<typename TAcquire>
|
||||
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;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#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_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}));
|
||||
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user