Skip to content
Open
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
10 changes: 10 additions & 0 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_kill PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_sigqueue_tests ${LIBS_INCLUDE_DIR}/tests/GuestSigqueue.cpp)
target_include_directories(guest_sigqueue_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_sigqueue_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_sigqueue_tests)
add_test(NAME guest_sigqueue COMMAND guest_sigqueue_tests)
set_tests_properties(guest_sigqueue PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_sigqueue PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_signal_set_tests ${LIBS_INCLUDE_DIR}/tests/GuestSignalSets.cpp)
target_include_directories(guest_signal_set_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_signal_set_tests PRIVATE libkernel libc)
Expand Down
8 changes: 8 additions & 0 deletions core/libs/prx/libkernel/System/src/Signals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,14 @@ int APS5_VABI kill_nid_postfix(int pid, int guest) {
if (pid != self && pid != 0 && pid != -self) { *__error_nid_postfix() = 3; return -1; }
return guest == 0 ? 0 : raise_nid_postfix(guest);
}
int APS5_VABI GuestSigqueue_nid_no_patch(int pid, int guest) {
if (static_cast<unsigned>(guest) > MaxSignal || pid <= 0) { *__error_nid_postfix() = 22; return -1; }
if (pid != getpid_nid_postfix()) { *__error_nid_postfix() = 3; return -1; }
return guest == 0 ? 0 : raise_nid_postfix(guest);
}
int APS5_VABI sigqueue_nid_disambig1_nid_postfix(int pid, int guest, void*) {
return GuestSigqueue_nid_no_patch(pid, guest);
}
int APS5_VABI killpg_nid_postfix(int pgrp, int guest) {
if (pgrp < 0) { *__error_nid_postfix() = 22; return -1; }
return kill_nid_postfix(-pgrp, guest);
Expand Down
5 changes: 0 additions & 5 deletions core/libs/prx/libkernel_sys/Export.Discovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2508,11 +2508,6 @@ int APS5_VABI settimeofday_nid_postfix(void) {
return 0;
}

int APS5_VABI sigqueue_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

int APS5_VABI sigreturn_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
Expand Down
6 changes: 6 additions & 0 deletions core/libs/prx/libkernel_sys/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ int APS5_VABI setsid_nid_postfix(void) {
return static_cast<int>(Fail(GuestEperm));
}

int APS5_VABI GuestSigqueue_nid_no_patch(int pid, int signal);

int APS5_VABI sigqueue_nid_postfix(int pid, int signal, void*) {
return GuestSigqueue_nid_no_patch(pid, signal);
}

std::uint16_t APS5_VABI umask_nid_postfix(std::uint16_t mask) {
return File::ExchangeCreationMask_nid_no_patch(mask);
}
Expand Down
63 changes: 63 additions & 0 deletions core/libs/tests/GuestSigqueue.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <csignal>
#include <cstdint>
#include <cstdlib>
#include <initializer_list>
using Handler = void (APS5_VABI *)(int);
using Sigqueue = int (APS5_VABI *)(int, int, void*);
extern "C" {
int APS5_VABI sigqueue_nid_postfix(int, int, void*);
int APS5_VABI sigqueue_nid_disambig1_nid_postfix(int, int, void*);
Handler APS5_VABI signal_nid_postfix(int, Handler);
int APS5_VABI getpid_nid_postfix(void);
int* APS5_VABI __error_nid_postfix();
}
volatile std::sig_atomic_t received = 0;
void APS5_VABI Callback(int value) { received = value; }
static void Require(bool value) { if (!value) std::abort(); }
static void Fails(Sigqueue sigqueue, int pid, int guest, int error) {
*__error_nid_postfix() = 0;
Require(sigqueue(pid, guest, nullptr) == -1 && *__error_nid_postfix() == error);
}
static void Succeeds(Sigqueue sigqueue, int pid, int guest, void* value) {
*__error_nid_postfix() = 0;
Require(sigqueue(pid, guest, value) == 0 && *__error_nid_postfix() == 0);
}
static void Check(Sigqueue sigqueue) {
const int self = getpid_nid_postfix();
const auto invalid = reinterpret_cast<Handler>(static_cast<std::uintptr_t>(-1));
const auto ignore = reinterpret_cast<Handler>(std::uintptr_t{1});
int payload = 7;
Succeeds(sigqueue, self, 0, &payload);
Succeeds(sigqueue, self, 0, nullptr);
for (int guest : {-1, 129}) {
Fails(sigqueue, self, guest, 22);
Fails(sigqueue, self + 1, guest, 22);
Fails(sigqueue, 0, guest, 22);
}
for (int target : {0, -1, -self}) {
Fails(sigqueue, target, 0, 22);
Fails(sigqueue, target, 15, 22);
}
for (int target : {self + 1, self + 1000}) {
Fails(sigqueue, target, 0, 3);
Fails(sigqueue, target, 15, 3);
}
Require(signal_nid_postfix(30, ignore) != invalid);
received = 0;
Succeeds(sigqueue, self, 30, &payload);
Require(received == 0);
Require(signal_nid_postfix(15, Callback) != invalid);
received = 0;
Succeeds(sigqueue, self, 15, &payload);
Require(received == 15);
received = 0;
Fails(sigqueue, self + 1, 15, 3);
Require(received == 0);
}

int main() {
Check(sigqueue_nid_postfix);
Check(sigqueue_nid_disambig1_nid_postfix);
return 0;
}
1 change: 1 addition & 0 deletions docs/dev/debt/libkernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@
- [File mappings on macOS](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) - `mmap` of a regular file, of an anonymous `shm_open` object (an unlinked temporary file on macOS) or of a JIT shared memory descriptor maps the descriptor shared or private as on Linux, with the same access checks. macOS refuses to create an executable file mapping (`EPERM`), so an executable mapping is made without execute and then given it with `mprotect`. Under Rosetta 2 (Apple M2, macOS 26), code rewritten through one view of a file after it ran from another view keeps its old translation until the executable view's protection changes. Named `shm_open` objects still throw on macOS.
- [sendfile](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) follows FreeBSD 11's `sendfile` for a connected stream socket: headers, the file from `offset` (to its end when `nbytes` is 0) and trailers, the file offset left unchanged, `EAGAIN` with `*sbytes` set after a partial send on a non-blocking socket. It reads the file through `sceKernelPread` in 64 KiB blocks and sends each with `send`. A socket as the file fails with `EINVAL`, as on FreeBSD, but any other descriptor `sceKernelPread` reads, such as `/dev/urandom`, is sent where FreeBSD requires a regular file or shared memory, and a file descriptor as the socket fails with `EBADF`, as for `send`, where FreeBSD answers `ENOTSOCK`. `flags` are ignored: reads complete before sending, so `SF_NODISKIO` never fails with `EBUSY`, and `SF_SYNC` returns once the data is queued on the socket rather than sent. PS5MC (PPSA99420) imports it
- [pthread_kill](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) returns the error number like FreeBSD's `pthread_kill`: `EINVAL` for signals outside 0 to 128 and `ESRCH` for a null thread. A signal to the calling thread goes through `raise`, so it shares `raise`'s limits; a signal to another thread is left on the pending set for whichever thread waits for or unblocks it, since it is not delivered to the chosen thread, and a handle that is not a live guest thread returns `ESRCH` without touching it
- [sigqueue](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel, libkernel_sys) drops its value: the signal is raised in the calling thread as `raise` does, so `si_code` and `si_value` are not set and an `SA_SIGINFO` handler throws, and the limit of 128 queued signals per process (`EAGAIN`) is not modelled. The checks follow FreeBSD 11's [`sys_sigqueue`](https://github.com/freebsd/freebsd-src/blob/34bd34750187bae02a0086402c78c031c59dd8b3/sys/kern/kern_sig.c#L1839-L1873): `EINVAL` for a signal above 128 or a pid of 0 or below, `ESRCH` for any process but the guest, and signal 0 sends nothing. both modules' exports call one libkernel helper
- [select](../../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) validates `tv_usec` but saturates a valid timeout beyond the remaining `steady_clock` range at `time_point::max()`, so the wait can continue effectively indefinitely. The console's behavior for a valid timeout outside the host clock's range is unknown.
- [inet_ntop, inet_pton](../../../core/libs/prx/libkernel/Socket/src/AddressConversion.cpp) (libkernel) - an `AF_INET6` result of `inet_ntop` is followed by zero bytes up to `size`. In a payload process on a PS5 (firmware 12.70), the text forms, the order of the checks and this padding were measured through both exports. The padding was sampled at sizes from 3 to 65536, above 64 only at 65, 128, 256, 1024, 4096 and 65536; the sizes that were not sampled, sizes above 65536 and a title process are assumed to behave the same
- [_fstatfs](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) fills the FreeBSD 11 `struct statfs` of the PS5 payload SDK with the host volume that holds the file: block size, transfer size, total, free and available blocks, maximum name length and the read-only, `nosuid` and `noexec` flags (on Windows only read-only, and `f_files` and `f_ffree` are 0, as NTFS has no fixed number of files). The filesystem type number and name, mount paths, owner, filesystem id and I/O counters are left 0 or empty, since they would describe a host mount, not a console one. Sockets and pipes return `EINVAL`, as FreeBSD's `getvnode` does; a Windows descriptor that is neither a file nor a pipe throws. Stremio Plus (PPSA74126) reads the free space before a download and only logs the other fields
Expand Down
Loading