From 4ebc7d7d4352df30647a5e2b32fd71fe3ddffcf2 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Sun, 18 Oct 2026 02:39:34 +0000 Subject: [PATCH] fix(libkernel): answer a join of the calling thread before its detach state scePthreadJoin checked whether the thread was detached before whether it was the calling thread. So a detached thread joining itself got EINVAL instead of EDEADLK, and so did the main thread, which scePthreadSelf adopts as detached. FreeBSD's join checks the calling thread right after the null check and before it looks at the detach state. The self check now comes first; the null, detached and already-joined answers are unchanged. Co-Authored-By: Claude Opus 5.5 --- .../libs/prx/libkernel/Pthread/src/Thread.cpp | 2 +- core/libs/tests/GuestPthreadJoinSelf.cpp | 26 +++++++++++++++++++ core/libs/tests/GuestThreadSelf.cpp | 3 ++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libkernel/Pthread/src/Thread.cpp b/core/libs/prx/libkernel/Pthread/src/Thread.cpp index b27a79bb5e..cba281fd82 100644 --- a/core/libs/prx/libkernel/Pthread/src/Thread.cpp +++ b/core/libs/prx/libkernel/Pthread/src/Thread.cpp @@ -830,9 +830,9 @@ int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, Pthread int APS5_VABI scePthreadJoin(Pthread thread, void** retval) { if (!thread) return SCE_KERNEL_ERROR_EINVAL; + if (thread == CurrentGuestState().current) return SCE_KERNEL_ERROR_EDEADLK; if (thread->_detached) return SCE_KERNEL_ERROR_EINVAL; if (thread->_joined.load(std::memory_order_acquire)) return SCE_KERNEL_ERROR_ESRCH; - if (thread == CurrentGuestState().current) return SCE_KERNEL_ERROR_EDEADLK; ThreadCancel::Check(); { std::unique_lock lock(thread->_join_mtx); diff --git a/core/libs/tests/GuestPthreadJoinSelf.cpp b/core/libs/tests/GuestPthreadJoinSelf.cpp index 742fa9d734..2fec26bef7 100644 --- a/core/libs/tests/GuestPthreadJoinSelf.cpp +++ b/core/libs/tests/GuestPthreadJoinSelf.cpp @@ -1,12 +1,16 @@ #include "SceTypes.hpp" +#include +#include #include #include +#include extern "C" { int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name); int APS5_VABI scePthreadJoin(Pthread thread, void** retval); Pthread APS5_VABI scePthreadSelf(void); int APS5_VABI pthread_join_nid_postfix(Pthread thread, void** retval); +int APS5_VABI scePthreadDetach(Pthread thread); } static constexpr int SCE_OK = 0; @@ -23,6 +27,18 @@ static void* APS5_VABI JoinSelf(void*) { return reinterpret_cast(std::uintptr_t{0x1234}); } +static std::atomic detachedChecks{0}; + +static void* APS5_VABI DetachThenJoinSelf(void*) { + void* result = reinterpret_cast(std::uintptr_t{0x66}); + Require(scePthreadDetach(scePthreadSelf()) == SCE_OK); + Require(scePthreadJoin(scePthreadSelf(), &result) == SCE_KERNEL_ERROR_EDEADLK); + Require(pthread_join_nid_postfix(scePthreadSelf(), &result) == GUEST_EDEADLK); + Require(result == reinterpret_cast(std::uintptr_t{0x66})); + detachedChecks.store(1); + return nullptr; +} + int main() { Pthread thread = nullptr; void* result = nullptr; @@ -36,4 +52,14 @@ int main() { Require(scePthreadCreate(&thread, &unset, JoinSelf, nullptr, "unset-attr") == SCE_OK); Require(scePthreadJoin(thread, &result) == SCE_OK); Require(result == reinterpret_cast(std::uintptr_t{0x1234})); + + thread = nullptr; + Require(scePthreadCreate(&thread, nullptr, DetachThenJoinSelf, nullptr, "detach-join-self") == SCE_OK); + for (int wait = 0; wait < 5000 && detachedChecks.load() == 0; ++wait) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + Require(detachedChecks.load() == 1); + + result = reinterpret_cast(std::uintptr_t{0x77}); + Require(scePthreadJoin(scePthreadSelf(), &result) == SCE_KERNEL_ERROR_EDEADLK); + Require(pthread_join_nid_postfix(scePthreadSelf(), &result) == GUEST_EDEADLK); + Require(result == reinterpret_cast(std::uintptr_t{0x77})); } diff --git a/core/libs/tests/GuestThreadSelf.cpp b/core/libs/tests/GuestThreadSelf.cpp index 9c59471e91..c94f557187 100644 --- a/core/libs/tests/GuestThreadSelf.cpp +++ b/core/libs/tests/GuestThreadSelf.cpp @@ -110,7 +110,8 @@ int main() { Require(pthread_getthreadid_np_nid_postfix() == static_cast(mainTid)); int local = 0; Require(StackContains(mainSelf, &local)); - Require(scePthreadJoin(mainSelf, nullptr) == SCE_KERNEL_ERROR_EINVAL); + Require(scePthreadJoin(mainSelf, nullptr) == SCE_KERNEL_ERROR_EDEADLK); + Require(pthread_join_nid_postfix(mainSelf, nullptr) == GUEST_EDEADLK); Require(scePthreadDetach(mainSelf) == SCE_KERNEL_ERROR_EINVAL); Require(scePthreadJoin(nullptr, nullptr) == SCE_KERNEL_ERROR_EINVAL); Require(scePthreadDetach(nullptr) == SCE_KERNEL_ERROR_EINVAL);