From 837c862e22c6fccd0264a79ffd8764d882153099 Mon Sep 17 00:00:00 2001 From: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Date: Sun, 11 Oct 2026 09:01:07 +0530 Subject: [PATCH] feat(libkernel): implement sigpending --- .../libs/prx/libkernel/System/src/Signals.cpp | 11 ++++++++ core/libs/tests/GuestSignals.cpp | 27 ++++++++++++++++--- docs/dev/debt/libkernel.md | 1 + 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/Signals.cpp b/core/libs/prx/libkernel/System/src/Signals.cpp index 01226d305..4629df138 100644 --- a/core/libs/prx/libkernel/System/src/Signals.cpp +++ b/core/libs/prx/libkernel/System/src/Signals.cpp @@ -583,6 +583,17 @@ int APS5_VABI pause_nid_postfix(void) { } } } + +int APS5_VABI sigpending_nid_postfix(GuestSignalSet* set) { + if (set == nullptr) { *__error_nid_postfix() = 14; return -1; } + auto& pending = Pending(); + std::lock_guard lock(pending.lock); + set->bits[0] = pending.words[0].load(std::memory_order_relaxed) & blockedMask.load(); + set->bits[1] = 0; + set->bits[2] = 0; + set->bits[3] = 0; + return 0; +} } extern "C" { diff --git a/core/libs/tests/GuestSignals.cpp b/core/libs/tests/GuestSignals.cpp index 44c0564b4..93a5311f5 100644 --- a/core/libs/tests/GuestSignals.cpp +++ b/core/libs/tests/GuestSignals.cpp @@ -7,6 +7,9 @@ #include #include using Handler = void (APS5_VABI *)(int); +struct GuestSignalSet { + std::uint32_t bits[4]; +}; extern "C" { Handler APS5_VABI signal_nid_postfix(int, Handler); int APS5_VABI raise_nid_postfix(int); @@ -18,10 +21,8 @@ int APS5_VABI pthread_sigmask_nid_postfix(int, const void*, void*); int* APS5_VABI __error_nid_postfix(); int APS5_VABI _is_signal_return_nid_postfix(std::uint64_t); unsigned int APS5_VABI GuestAlarm_nid_no_patch(unsigned int); +int APS5_VABI sigpending_nid_postfix(GuestSignalSet*); } -struct GuestSignalSet { - std::uint32_t bits[4]; -}; struct GuestSigaction { std::uintptr_t handler; int flags; @@ -200,4 +201,24 @@ int main() { Require(sigaction_nid_postfix(13, nullptr, ¤t) == 0 && current.handler == static_cast(-1) && current.flags == 0x2); Require(signal_nid_postfix(13, nullptr) == invalid); Require(sigaction_nid_postfix(13, nullptr, ¤t) == 0 && current.handler == 0); + *__error_nid_postfix() = 0; + Require(sigpending_nid_postfix(nullptr) == -1); + Require(*__error_nid_postfix() == 14); + *__error_nid_postfix() = 42; + GuestSignalSet pendingSignals{{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}; + Require(sigpending_nid_postfix(&pendingSignals) == 0); + Require(pendingSignals.bits[0] == 0 && pendingSignals.bits[1] == 0 && pendingSignals.bits[2] == 0 && pendingSignals.bits[3] == 0); + Require(*__error_nid_postfix() == 42); + Require(signal_nid_postfix(15, nullptr) == Callback); + Require(signal_nid_postfix(15, Callback) == nullptr); + received = 0; + Require(sigprocmask_nid_postfix(1, &termMask, nullptr) == 0); + Require(raise_nid_postfix(15) == 0 && received == 0); + pendingSignals = {{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}}; + Require(sigpending_nid_postfix(&pendingSignals) == 0); + Require(pendingSignals.bits[0] == (1u << 14) && pendingSignals.bits[1] == 0 && pendingSignals.bits[2] == 0 && pendingSignals.bits[3] == 0); + Require(sigprocmask_nid_postfix(2, &termMask, nullptr) == 0 && received == 15); + Require(sigpending_nid_postfix(&pendingSignals) == 0); + Require(pendingSignals.bits[0] == 0 && pendingSignals.bits[1] == 0 && pendingSignals.bits[2] == 0 && pendingSignals.bits[3] == 0); + Require(signal_nid_postfix(15, nullptr) == Callback); } diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 2e5574be7..0f7851b4d 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -124,6 +124,7 @@ - [sceKernelRaiseException](../../../core/libs/prx/libkernel/System/src/Exception.cpp) reaches a thread that waits in an alertable `TimedWait` wait through an APC. A thread blocked anywhere else, including all waits under `APS5_COARSE_TIMED_WAITS` and host waits inside the emulator, is redirected and runs the handler only when that wait returns - [sceKernelRaiseException](../../../core/libs/prx/libkernel/System/src/Exception.cpp) on Windows runs the handler wherever the target thread was interrupted. It waits only while the thread is inside winpthreads or restoring a context through `NtContinue`, and throws if that lasts 1 s, so a handler that calls into other host code the thread was interrupted in, such as the C runtime's heap or a lock of the emulator, can deadlock on it or reenter it - [sigaltstack](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records each thread's alternate signal stack and reports it back, following FreeBSD's `kern_sigaltstack` (`EINVAL` for flags other than `SS_DISABLE`, `ENOMEM` below `MINSIGSTKSZ` 2048), but handlers never run on it, so `SS_ONSTACK` and `EPERM` are never reported +- [sigpending](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) reports the intersection of the process-wide pending set of `sigtimedwait` and the blocked mask of `_sigprocmask`, as FreeBSD's `sys_sigpending` intersects the pending list with `td_sigmask`, and returns -1 with `EFAULT` for a null set. Because `_sigprocmask` keeps only the first 32 signals, signals 33 to 128 are never blocked and never appear in the set, and setting `SIG_IGN` (or `SIG_DFL` on a signal whose default action is to ignore) does not clear a signal that was already marked pending. PPSA99420 imports it - [scePthreadJoin, scePthreadCreate](../../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) - on a PS5 (firmware 13.60) joining a thread a second time returns `ESRCH` (`0x80020003`); here a joined thread is released, so a second join reads freed memory. A null entry crashed the payload and still throws. Joining the calling thread returns `EDEADLK` and a zeroed attribute creates a default thread, as on the console - [Thread cancellation](../../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) follows FreeBSD libthr (states and types 0/1 and 0/2, `PTHREAD_CANCELED` 1, the `_pthread_cleanup_info` node layout), not measured on the console. A request is acted on only in `scePthreadTestcancel`, the condition variable, `sem` and `scePthreadSem` waits, `scePthreadJoin` and the sleeps, and their POSIX names; file and socket calls are not cancellation points. A sleep is not interrupted: the thread exits when it ends. The asynchronous type acts like the deferred one, except at the two set calls. - [cpuset_setaffinity](../../../core/libs/prx/libkernel/Pthread/Posix/Thread.cpp) (libkernel) - only the calling thread or the calling process is handled. Bits outside `sceKernelGetAvailableCpumask` are dropped, and a mask with none of those bits fails with `EDEADLK`, as FreeBSD does when the new set has no CPU in common with the parent set