Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,26 @@ if(BUILD_TESTING)
set_tests_properties(guest_pthread_join_detach PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_handle_after_exit_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadHandleAfterExit.cpp)
target_include_directories(guest_pthread_handle_after_exit_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_handle_after_exit_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_pthread_handle_after_exit_tests)
add_test(NAME guest_pthread_handle_after_exit COMMAND guest_pthread_handle_after_exit_tests)
set_tests_properties(guest_pthread_handle_after_exit PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_pthread_handle_after_exit PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_detach_attr_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadDetachAttr.cpp)
target_include_directories(guest_pthread_detach_attr_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_detach_attr_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_pthread_detach_attr_tests)
add_test(NAME guest_pthread_detach_attr COMMAND guest_pthread_detach_attr_tests)
set_tests_properties(guest_pthread_detach_attr PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_pthread_detach_attr PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_pthread_attr_errors_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadAttrErrors.cpp)
target_include_directories(guest_pthread_attr_errors_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_pthread_attr_errors_tests PRIVATE libkernel libc)
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libkernel/Pthread/include/Pthread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ struct PthreadPrivate {
std::string name;
std::atomic<bool> _finished;
void* _retval;
bool _detached;
std::atomic<bool> _detached;
bool _adopted;
Pthread* _handleStorage;
std::atomic<bool> _joined;
Expand Down
19 changes: 19 additions & 0 deletions core/libs/prx/libkernel/Pthread/src/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,15 @@ std::unique_lock<std::mutex> LockLiveGuestThread(const PthreadPrivate* thread) {
return {};
}

static std::unique_lock<std::mutex> LockRegisteredGuestThread(const PthreadPrivate* thread) {
auto& registry = CpuClocks();
std::unique_lock lock(registry.lock);
for (const auto& [id, registered] : registry.threads) {
if (registered == thread) return lock;
}
return {};
}

bool GuestThreadIsLive(const PthreadPrivate* thread) {
return LockLiveGuestThread(thread).owns_lock();
}
Expand Down Expand Up @@ -1272,12 +1281,16 @@ KernelCpumask APS5_VABI sceKernelGetAvailableCpumask(void) {

int APS5_VABI scePthreadGetaffinity(Pthread thread, KernelCpumask* mask) {
if (!thread || !mask) return SCE_KERNEL_ERROR_EINVAL;
const auto registered = LockRegisteredGuestThread(thread);
if (!registered) return SCE_KERNEL_ERROR_ESRCH;
*mask = thread->affinity.load(std::memory_order_relaxed);
return SCE_OK;
}

int APS5_VABI scePthreadGetname(Pthread thread, char* name) {
if (!thread || !name) return SCE_KERNEL_ERROR_EINVAL;
const auto registered = LockRegisteredGuestThread(thread);
if (!registered) return SCE_KERNEL_ERROR_ESRCH;
std::lock_guard lock(thread->nameLock);
const std::size_t length = std::min<std::size_t>(thread->name.size(), THREAD_NAME_CAPACITY - 1);
std::memcpy(name, thread->name.data(), length);
Expand All @@ -1287,6 +1300,8 @@ int APS5_VABI scePthreadGetname(Pthread thread, char* name) {

int APS5_VABI scePthreadGetprio(Pthread thread, int* prio) {
if (!thread || !prio) return SCE_KERNEL_ERROR_EINVAL;
const auto registered = LockRegisteredGuestThread(thread);
if (!registered) return SCE_KERNEL_ERROR_ESRCH;
*prio = thread->priority.load(std::memory_order_relaxed);
return SCE_OK;
}
Expand All @@ -1297,6 +1312,8 @@ int APS5_VABI scePthreadGetthreadid(void) {

int APS5_VABI scePthreadRename(Pthread thread, const char* name) {
if (!thread || !name) return SCE_KERNEL_ERROR_EINVAL;
const auto registered = LockRegisteredGuestThread(thread);
if (!registered) return SCE_KERNEL_ERROR_ESRCH;
std::lock_guard lock(thread->nameLock);
thread->name = name;
#ifdef _WIN32
Expand All @@ -1309,6 +1326,8 @@ int APS5_VABI scePthreadRename(Pthread thread, const char* name) {

int APS5_VABI scePthreadSetaffinity(Pthread thread, KernelCpumask mask) {
if (!thread || mask == 0) return SCE_KERNEL_ERROR_EINVAL;
const auto registered = LockRegisteredGuestThread(thread);
if (!registered) return SCE_KERNEL_ERROR_ESRCH;
thread->affinity.store(mask, std::memory_order_relaxed);
return SCE_OK;
}
Expand Down
57 changes: 57 additions & 0 deletions core/libs/tests/GuestPthreadDetachAttr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/Pthread/include/Pthread.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <thread>

static constexpr int SCE_OK = 0;
static constexpr int ROUNDS = 50;
static constexpr int DETACHED = 1;

static void Require(bool condition) { if (!condition) std::abort(); }

struct Worker {
std::atomic<bool> release{false};
std::atomic<bool> finished{false};
};

static void* APS5_VABI Wait(void* opaque) {
auto* worker = static_cast<Worker*>(opaque);
while (!worker->release.load()) std::this_thread::yield();
worker->finished.store(true);
return nullptr;
}

int main() {
for (int round = 0; round < ROUNDS; ++round) {
Worker worker;
Pthread thread = nullptr;
Require(scePthreadCreate(&thread, nullptr, Wait, &worker, "detach_attr") == SCE_OK);
std::atomic<bool> observing{true};
std::atomic<bool> started{false};
std::thread observer([&] {
PthreadAttr attr = nullptr;
Require(scePthreadAttrInit(&attr) == SCE_OK);
while (observing.load()) {
Require(scePthreadAttrGet(thread, &attr) == SCE_OK);
started.store(true);
}
Require(scePthreadAttrDestroy(&attr) == SCE_OK);
});
while (!started.load()) std::this_thread::yield();
Require(scePthreadDetach(thread) == SCE_OK);
PthreadAttr attr = nullptr;
Require(scePthreadAttrInit(&attr) == SCE_OK);
Require(scePthreadAttrGet(thread, &attr) == SCE_OK);
int state = -1;
Require(scePthreadAttrGetdetachstate(&attr, &state) == SCE_OK && state == DETACHED);
Require(scePthreadAttrDestroy(&attr) == SCE_OK);
observing.store(false);
observer.join();
worker.release.store(true);
while (!worker.finished.load()) std::this_thread::yield();
}
return 0;
}
71 changes: 71 additions & 0 deletions core/libs/tests/GuestPthreadHandleAfterExit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "prx/libkernel/include/Api.hpp"
#include "prx/libkernel/Pthread/include/Pthread.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <thread>

static constexpr int SCE_OK = 0;

static void Require(bool condition) { if (!condition) std::abort(); }

struct Worker {
std::atomic<bool> started{false};
std::atomic<bool> release{false};
};

static void* APS5_VABI Wait(void* opaque) {
auto* worker = static_cast<Worker*>(opaque);
worker->started.store(true);
while (!worker->release.load()) std::this_thread::yield();
return nullptr;
}

static void RequireAlive(Pthread thread) {
char name[32];
int priority = 0;
KernelCpumask mask = 0;
Require(scePthreadGetname(thread, name) == SCE_OK);
Require(scePthreadGetprio(thread, &priority) == SCE_OK);
Require(scePthreadGetaffinity(thread, &mask) == SCE_OK && mask != 0);
Require(scePthreadSetaffinity(thread, mask) == SCE_OK);
Require(scePthreadRename(thread, "renamed") == SCE_OK);
}

static void RequireEnded(Pthread thread) {
char name[32];
int priority = 0;
KernelCpumask mask = 0;
Require(scePthreadGetname(thread, name) == SCE_KERNEL_ERROR_ESRCH);
Require(scePthreadGetprio(thread, &priority) == SCE_KERNEL_ERROR_ESRCH);
Require(scePthreadGetaffinity(thread, &mask) == SCE_KERNEL_ERROR_ESRCH);
Require(scePthreadSetaffinity(thread, 1) == SCE_KERNEL_ERROR_ESRCH);
Require(scePthreadRename(thread, "renamed") == SCE_KERNEL_ERROR_ESRCH);
}

static void WaitUntilReleased(Pthread thread) {
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
int priority = 0;
while (scePthreadGetprio(thread, &priority) != SCE_KERNEL_ERROR_ESRCH) {
Require(std::chrono::steady_clock::now() < deadline);
std::this_thread::yield();
}
}

int main() {
RequireAlive(scePthreadSelf());

for (int round = 0; round < 20; ++round) {
Worker detached;
Pthread detachedThread = nullptr;
Require(scePthreadCreate(&detachedThread, nullptr, Wait, &detached, "detached") == SCE_OK);
while (!detached.started.load()) std::this_thread::yield();
RequireAlive(detachedThread);
Require(scePthreadDetach(detachedThread) == SCE_OK);
detached.release.store(true);
WaitUntilReleased(detachedThread);
RequireEnded(detachedThread);
}
return 0;
}
2 changes: 1 addition & 1 deletion docs/dev/debt/libkernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
- [__progname](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export
- [environ](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - data export pointing to a fixed empty environment so titles that import it load; it is not the environment libc's `getenv` reads, so it lists none of those variables and `setenv`/`putenv` changes do not appear in it
- [seteuid, setegid, setuid, setgid, setreuid, setregid, setresuid, setresgid, setgroups, getgroups](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the guest process is assumed to run as root, with real, effective and saved user and group ids 0. As in FreeBSD's `sys_seteuid`, `sys_setegid`, `sys_setuid`, `sys_setgid`, `sys_setreuid`, `sys_setregid`, `sys_setresuid` and `sys_setresgid`, id 0 (or -1 for an unchanged `setreuid`/`setregid`/`setresuid`/`setresgid` id) is then accepted without a privilege check and changes no credential, so 0 is returned; any other id would change an id, which is not modelled, and throws. `setgroups` keeps the supplementary group list as `kern_setgroups` does (`NGROUPS_MAX` 1023, an empty list leaves the effective group as the only entry, a first entry other than 0 would change the effective group and throws) and `getgroups` reports it. The ids and privileges a title has on a console are unverified
- [pthread_getname_np](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - a thread that has already been joined returns `ESRCH` (3) and its output buffer is left untouched, as measured on a PS5 (firmware 13.60) by the author of this entry; since [#4481](https://github.com/boykopovar/AnyPS5/pull/4481) the joined thread's object is retained, so the posix wrapper answers `ESRCH` for it instead of reading the retained name. A null name buffer is answered with `EFAULT` before that, an order the console table does not measure. The SCE sibling `scePthreadGetname` has no measured joined-thread behaviour on the console and still reads the retained object's name, so it is unchanged, as is the pre-existing dangling case of a thread that ended detached (its object is released at exit; see the scePthreadJoin entry)
- [pthread_getname_np](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - a thread that has already been joined returns `ESRCH` (3) and its output buffer is left untouched, as measured on a PS5 (firmware 13.60) by the author of this entry; since [#4481](https://github.com/boykopovar/AnyPS5/pull/4481) the joined thread's object is retained, so the posix wrapper answers `ESRCH` for it instead of reading the retained name. A null name buffer is answered with `EFAULT` before that, an order the console table does not measure. The SCE sibling `scePthreadGetname` has no measured joined-thread behaviour on the console and still reads the retained object's name. After a detached thread is released, it now returns `ESRCH` through the thread registry instead of reading freed storage; that released-handle answer is not measured on the console (see the scePthreadJoin entry)
- [sceKernelGetOperationMode](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call and ignores the result. A homebrew payload on a PS5 (firmware 13.60) reads mode 2 and submode 0, nothing else is written, and the result register holds no status, so the function is taken to return nothing. Whether a title process reads the same values is unverified. Null outputs throw. In PPSA12544 the call at 0x16a5889 is followed by a log of both values and a comparison of the second with 1, and when it equals 1 a byte of the title is set; a second call (0x67e404) is made from a function that passes its caller's arguments through, and neither reads the result.
- [sceCoredumpGetStopInfoCpu](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) has no active coredump context. Native null/size validation is recovered, including both 8-byte and 16-byte records; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output. Active-handler data retrieval remains unimplemented. One title (PPSA02225) disassembled for this entry calls it once (arguments set up at 0x8d67dd and 0x8d67e1) with a stack buffer and the length 0x10: for 0x81180005 it branches away, for 0 it reads a 32-bit code at offset 8 of the buffer, and for any other nonzero result, which includes the `NOT_IN_COREDUMP_HANDLER` result returned here, it formats an error text containing the code. The call does not show what the console returns outside a coredump handler.
- [sceCoredumpGetThreadContextInfo](../../../core/libs/prx/libkernel/System/src/Coredump.cpp) validates a nonnull 144-byte output using the native checks. Active-handler register capture remains unimplemented; valid requests return `NOT_IN_COREDUMP_HANDLER` without accessing the output.
Expand Down
Loading