From 6cbb4ff7fd81271de710d65a9ff0784fc7b4ee28 Mon Sep 17 00:00:00 2001 From: Josip Parlov Date: Sun, 25 Oct 2026 23:03:10 +0100 Subject: [PATCH] fix(libSceNet): read an option of a libkernel socket through sceNetGetsockopt Human Fall Flat (PPSA02429) opens its sockets with libkernel's socket() and reads options of them with sceNetGetsockopt. libSceNet only looked in its own socket table, so each call failed with SCE_NET_ERROR_EBADF. On the console both calls work on the same kernel sockets, the position sceNetSetsockopt already takes for MXGP 26 (PPSA30839). A descriptor libSceNet does not own but libkernel has open as a socket now goes to libkernel's getsockopt(), which answers the options it models. Anything else still fails with EBADF. The fallback runs after g_mutex is released. guest_sce_net reads SO_SNDBUF and TCP_NODELAY of a libkernel TCP socket through sceNetGetsockopt, checks that an unknown level fails with EINVAL, and that the call fails with EBADF once the socket is closed. --- core/libs/prx/libSceNet/Export.cpp | 11 +++++++++-- core/libs/tests/GuestSceNet.cpp | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index ce94422d82..9bed366baf 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -1464,11 +1464,18 @@ int APS5_VABI sceNetSetsockopt(int s, int level, int optname, const void* optval int APS5_VABI sceNetGetsockopt(int s, int level, int optname, void* optval, uint32_t* optlen) { Sock socket; + bool found = false; { std::lock_guard lk(g_mutex); auto it = g_socks.find(s); - if (it == g_socks.end()) return fail(NET_EBADF); - socket = it->second; + if (it != g_socks.end()) { + socket = it->second; + found = true; + } + } + if (!found) { + if (!GuestSocketIsOpen_nid_no_patch(s)) return fail(NET_EBADF); + return getsockopt_nid_postfix(s, level, optname, optval, optlen) == 0 ? 0 : fail(*__error_nid_postfix()); } if (optval == nullptr || optlen == nullptr) return fail(NET_EINVAL); if (level == NET_SOL_SOCKET && optname == NET_SO_PROTOCOL) { diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 67ef1daf4a..b24b03ec3e 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -1508,6 +1508,27 @@ static void CheckCloseKernelSocket() { Require(Failed(sceNetSocketClose(kernel), 9)); } +static void CheckKernelSocketGetOption() { + const int kernel = socket_nid_postfix(2, 1, 0); + Require(kernel >= 0); + const int requested = 65536; + Require(sceNetSetsockopt(kernel, 0xffff, 0x1001, &requested, sizeof(requested)) == 0); + int value = 0; + std::uint32_t size = sizeof(value); + Require(sceNetGetsockopt(kernel, 0xffff, 0x1001, &value, &size) == 0 && size == sizeof(value) && value > 0); + int expected = 0; + std::uint32_t expectedSize = sizeof(expected); + Require(getsockopt_nid_postfix(kernel, 0xffff, 0x1001, &expected, &expectedSize) == 0 && value == expected); + const int one = 1; + Require(sceNetSetsockopt(kernel, 6, 1, &one, sizeof(one)) == 0); + value = 0; + size = sizeof(value); + Require(sceNetGetsockopt(kernel, 6, 1, &value, &size) == 0 && value != 0); + Require(Failed(sceNetGetsockopt(kernel, 1234, 1, &value, &size), 22)); + Require(close_nid_postfix(kernel) == 0); + Require(Failed(sceNetGetsockopt(kernel, 0xffff, 0x1001, &value, &size), 9)); +} + static std::array RequireSockInfo(int socket, int type, int flags, const std::array& local, const std::array& remote) { std::array records; @@ -1897,6 +1918,7 @@ int main() { } CheckDatagramDisconnect(); CheckCloseKernelSocket(); + CheckKernelSocketGetOption(); CheckSockInfo(); CheckSockInfo6();