mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-06 13:30:00 +00:00
fix(libkernel): getsockopt reads options of libSceNet sockets
setsockopt reaches a socket created with sceNetSocket through the option setter libSceNet registers, but getsockopt looked the descriptor up only among libkernel's own sockets and failed, so a title could set an option on such a socket through the BSD call and not read it back. libSceNet now registers an option getter with libkernel next to its setter, which answers through sceNetGetsockopt, so SO_RCVTIMEO and SO_SNDTIMEO come back as a struct timeval.
This commit is contained in:
1 parent
e887616e74
commit
aea804d9fd
6 files changed
+41
-1
No files matched your search
@@ -892,6 +892,19 @@ bool SetSocketOptionForKernel(int descriptor, int level, int option, const void*
|
||||
|
||||
const bool g_socketOptionSetterRegistered = (KernelSetSocketOptionSetter_nid_no_patch(&SetSocketOptionForKernel), true);
|
||||
|
||||
extern "C" int APS5_VABI sceNetGetsockopt(int s, int level, int optname, void* optval, uint32_t* optlen);
|
||||
|
||||
bool GetSocketOptionForKernel(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error) {
|
||||
{
|
||||
std::lock_guard lk(g_mutex);
|
||||
if (g_socks.count(descriptor) == 0) return false;
|
||||
}
|
||||
*error = sceNetGetsockopt(descriptor, level, option, value, length) == 0 ? 0 : *errno_slot();
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool g_socketOptionGetterRegistered = (KernelSetSocketOptionGetter_nid_no_patch(&GetSocketOptionForKernel), true);
|
||||
|
||||
constexpr int GuestFcntlGetStatus = 3;
|
||||
constexpr int GuestFcntlSetStatus = 4;
|
||||
constexpr int GuestReadWrite = 2;
|
||||
|
||||
@@ -19,6 +19,7 @@ struct Entry {
|
||||
using Poller = int (*)(Entry* entries, int count, int timeoutMilliseconds);
|
||||
using Closer = bool (*)(int descriptor, int* error);
|
||||
using OptionSetter = bool (*)(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error);
|
||||
using OptionGetter = bool (*)(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error);
|
||||
using StatusControl = bool (*)(int descriptor, int command, int flags, int* result, int* error);
|
||||
struct NativeOwner {
|
||||
int (*transfer)(NativeOwner* owner, bool send, void* buffer, int length) = nullptr;
|
||||
@@ -32,6 +33,8 @@ extern "C" void KernelSetSocketCloser_nid_no_patch(KernelSocketPoll::Closer clos
|
||||
extern "C" bool KernelCloseSocket_nid_no_patch(int descriptor, int* error);
|
||||
extern "C" void KernelSetSocketOptionSetter_nid_no_patch(KernelSocketPoll::OptionSetter setter);
|
||||
extern "C" bool KernelSetSocketOption_nid_no_patch(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error);
|
||||
extern "C" void KernelSetSocketOptionGetter_nid_no_patch(KernelSocketPoll::OptionGetter getter);
|
||||
extern "C" bool KernelGetSocketOption_nid_no_patch(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error);
|
||||
extern "C" void KernelSetSocketStatusControl_nid_no_patch(KernelSocketPoll::StatusControl control);
|
||||
extern "C" bool KernelControlSocketStatus_nid_no_patch(int descriptor, int command, int flags, int* result, int* error);
|
||||
extern "C" void KernelSetSocketNativeLookup_nid_no_patch(KernelSocketPoll::NativeLookup lookup);
|
||||
|
||||
@@ -38,6 +38,7 @@ struct GuestTimeval {
|
||||
std::atomic<KernelSocketPoll::Poller> g_socketPoller{nullptr};
|
||||
std::atomic<KernelSocketPoll::Closer> g_socketCloser{nullptr};
|
||||
std::atomic<KernelSocketPoll::OptionSetter> g_socketOptionSetter{nullptr};
|
||||
std::atomic<KernelSocketPoll::OptionGetter> g_socketOptionGetter{nullptr};
|
||||
std::atomic<KernelSocketPoll::StatusControl> g_socketStatusControl{nullptr};
|
||||
std::atomic<KernelSocketPoll::NativeLookup> g_socketNativeLookup{nullptr};
|
||||
|
||||
@@ -161,6 +162,15 @@ void KernelSetSocketOptionSetter_nid_no_patch(KernelSocketPoll::OptionSetter set
|
||||
g_socketOptionSetter.store(setter);
|
||||
}
|
||||
|
||||
void KernelSetSocketOptionGetter_nid_no_patch(KernelSocketPoll::OptionGetter getter) {
|
||||
g_socketOptionGetter.store(getter);
|
||||
}
|
||||
|
||||
bool KernelGetSocketOption_nid_no_patch(int descriptor, int level, int option, void* value, std::uint32_t* length, int* error) {
|
||||
const auto getter = g_socketOptionGetter.load();
|
||||
return getter != nullptr && getter(descriptor, level, option, value, length, error);
|
||||
}
|
||||
|
||||
bool KernelSetSocketOption_nid_no_patch(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error) {
|
||||
const auto setter = g_socketOptionSetter.load();
|
||||
return setter != nullptr && setter(descriptor, level, option, value, length, error);
|
||||
|
||||
@@ -1102,6 +1102,10 @@ int APS5_VABI setsockopt_nid_postfix(int descriptor, int level, int option,
|
||||
}
|
||||
int APS5_VABI getsockopt_nid_postfix(int descriptor, int level, int option,
|
||||
void* value, std::uint32_t* length) {
|
||||
int netError = 0;
|
||||
if (!GuestSockets::IsOpen(descriptor) && KernelGetSocketOption_nid_no_patch(descriptor, level, option, value, length, &netError)) {
|
||||
return netError == 0 ? 0 : Fail(netError);
|
||||
}
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
if (level != 0xffff && level != 6 && level != 41) return Fail(42);
|
||||
|
||||
@@ -91,6 +91,7 @@ int APS5_VABI bind_nid_postfix(int, const void*, std::uint32_t);
|
||||
int APS5_VABI close_nid_postfix(int);
|
||||
bool GuestSocketIsOpen_nid_no_patch(int);
|
||||
int APS5_VABI setsockopt_nid_postfix(int, int, int, const void*, std::uint32_t);
|
||||
int APS5_VABI getsockopt_nid_postfix(int, int, int, void*, std::uint32_t*);
|
||||
int APS5_VABI fcntl_nid_postfix(int, int, ...);
|
||||
struct GuestPollEntry {
|
||||
int descriptor;
|
||||
@@ -1515,6 +1516,15 @@ int main() {
|
||||
std::uint32_t optionSize = sizeof(option);
|
||||
Require(sceNetGetsockopt(closed, 0xffff, 0x20, &option, &optionSize) == 0 && option == 0x20 && optionSize == sizeof(option));
|
||||
Require(sceNetGetsockopt(closed, 0xffff, 0x4, &option, &optionSize) == 0 && option == 0x4);
|
||||
option = 0;
|
||||
optionSize = sizeof(option);
|
||||
Require(getsockopt_nid_postfix(closed, 0xffff, 0x20, &option, &optionSize) == 0 && option == 0x20 && optionSize == sizeof(option));
|
||||
KernelTimeval receiveTimeout{0, 250000};
|
||||
Require(setsockopt_nid_postfix(closed, 0xffff, 0x1006, &receiveTimeout, sizeof(receiveTimeout)) == 0);
|
||||
receiveTimeout = {};
|
||||
std::uint32_t timeoutSize = sizeof(receiveTimeout);
|
||||
Require(getsockopt_nid_postfix(closed, 0xffff, 0x1006, &receiveTimeout, &timeoutSize) == 0 && timeoutSize == sizeof(receiveTimeout));
|
||||
Require(receiveTimeout.tv_sec == 0 && receiveTimeout.tv_usec == 250000);
|
||||
Require(close_nid_postfix(closed) == 0);
|
||||
Require(Failed(sceNetSocketClose(closed), 9));
|
||||
CheckKernelTimeouts();
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
- [Shared memory](../../../core/libs/prx/libkernel/File/src/SharedMemory.cpp) (libkernel) on Linux implements anonymous and named objects, bounded descriptor writes and seeks, and `shm_unlink` with effective read/write permission checks from FreeBSD 11. Named paths up to 1023 bytes use `/anyps5-shm-` plus SHA-1; collisions are not detected, and the namespace is shared across processes in the host mount namespace. Named creation applies the guest umask without changing the host mask. Linux procfs and `/dev/shm` are required. Operations use an advisory directory lock; external host operations bypass it, unrelated objects serialize, and descriptor operations require two spare host descriptors and compare device/inode with the initial descriptor identity on both the pinned-object and ordinary-file fallback paths. [Guest mappings](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) support shared and private mappings of these descriptors through the allocation tracker and pin a duplicate descriptor during validation and mapping; mapping requires one spare host descriptor; file-backed mappings use the write-watch scan fallback because aliases and descriptor writes bypass per-mapping tracking. On Windows, anonymous objects are delete-on-close temporary files and `mmap` maps them as [regular files](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp); named objects remain unsupported. Invalid non-null name pointers, console behaviour and title compatibility remain unverified.
|
||||
- [sceKernelAddReadEvent and sceKernelAddWriteEvent](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel): a read event on a listening socket, here and in `kevent`, reports the number of completed connections waiting to be accepted in `data`, as FreeBSD 11.4's [`filt_solisten`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L3384-L3392) reports `so_qlen`: on Linux a TCP listener's accept queue from `TCP_INFO` (`tcpi_unacked`, which Linux fills with the accept backlog for a listening socket), and elsewhere, or for a listener that is not TCP, 1 while a connection is waiting, as no host call reports that count. A write event reports the free space of the socket's send buffer in `data`, as FreeBSD 11.4's [`filt_sowrite`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c#L3358-L3382) reports `sbspace`: on Linux the host's `SO_SNDBUF` less the bytes `SIOCOUTQ` counts as queued, which include the host's per-packet overhead, so the space shrinks by somewhat more than the bytes sent. Windows has no query for that space and macOS is not wired up, so both report the whole `SO_SNDBUF`, which does not shrink as data is queued. Only guest sockets are supported; other descriptors and a low watermark above 1 throw
|
||||
- [sceKernelAddReadEvent on pipes](../../../core/libs/prx/libkernel/Equeue/Equeue.cpp) (libkernel) is the one exception to the sockets-only rule above: it also takes the read end of a pipe, as `kevent` does through FreeBSD's `filt_piperead`, and ignores the size argument there. Host streams, other descriptors and a write event on a pipe throw
|
||||
- [close](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) and [setsockopt](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) reach a [libSceNet](../../../core/libs/prx/libSceNet/Export.cpp) socket through hooks libSceNet registers, as `select` polls them; the other libkernel socket calls do not. libSceNet numbers its sockets from 32 in its own table, so a CRT file descriptor and a socket can share a number; `close` then closes the socket. On the console both are one kernel descriptor space
|
||||
- [close](../../../core/libs/prx/libkernel/File/src/Stdio.cpp), [setsockopt and getsockopt](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) reach a [libSceNet](../../../core/libs/prx/libSceNet/Export.cpp) socket through hooks libSceNet registers, as `select` polls them; the other libkernel socket calls do not. libSceNet numbers its sockets from 32 in its own table, so a CRT file descriptor and a socket can share a number; `close` then closes the socket. On the console both are one kernel descriptor space
|
||||
- [sceKernelPread, sceKernelPwrite, sceKernelPreadv, sceKernelPwritev](../../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) on Windows transfer at the given offset with an overlapped `ReadFile`/`WriteFile` and restore the descriptor's file pointer afterwards. Guest scalar transfers, positioned transfers, vectored transfers and seeks share a position lock across duplicated descriptors; a vector holds it for the whole operation. Independently opened descriptors and other processes do not share that lock, so vector transfers over their overlapping file ranges are not coordinated. Handles that are not disk files return `ESPIPE`. A pipe or console `readv` can return a short read when no more input is available; each iovec is transferred separately, so a failure after the first transfer returns the bytes already done
|
||||
- [sceKernelAioSubmitReadCommands, sceKernelAioSubmitWriteCommands and their Multiple variants](../../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) on Windows transfer through the same positioned path as `sceKernelPread`/`sceKernelPwrite` and restore the descriptor's file pointer afterwards; positioned transfers, plain transfers and seeks of one descriptor are serialized per descriptor (shared across duplicated descriptors), so a concurrent transfer observes a stable file pointer
|
||||
- [sceKernelAioSetParam](../../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) writes the five values into the 20-byte scheduling struct the first argument points to, following the declarations in [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json), [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelAio.cs) and [ampr_emu](https://github.com/drakmor/ampr_emu/blob/cfa85df379f6eeeb165d7badf9b648e266fe77b7/ps5/include/sce/_fs.h). Whether the console checks the values is unknown, so a null struct, a scheduling window or delayed count limit outside 0..128, a split flag other than 0 or 1 and a split size or chunk size above 0x1000000 (SharpProspero's maximums) throw. The scheduling settings are not used: `sceKernelAioInitializeImpl` ignores the struct and requests run synchronously on submit
|
||||
|
||||
Reference in new issue
Block a user