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.
This commit is contained in:
Josip Parlov committed 2026-10-25 23:28:23 +01:00
1 parent b90d009d9f
commit 6cbb4ff7fd
2 files changed
+31 -2

No files matched your search

+9 -2
View File
@@ -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) {
+22
View File
@@ -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<std::uint8_t, 0xa0> RequireSockInfo(int socket, int type, int flags, const std::array<std::uint8_t, 16>& local,
const std::array<std::uint8_t, 16>& remote) {
std::array<std::uint8_t, 0x140> records;
@@ -1897,6 +1918,7 @@ int main() {
}
CheckDatagramDisconnect();
CheckCloseKernelSocket();
CheckKernelSocketGetOption();
CheckSockInfo();
CheckSockInfo6();