From 22827bf6ad532c012cb7d39e002af7ea72dc4ac1 Mon Sep 17 00:00:00 2001 From: jdmagic Date: Fri, 9 Oct 2026 13:50:06 -0400 Subject: [PATCH] fix(libSceNet): sceNetEpollControl takes 2 as MOD and 3 as DEL sceNetEpollControl used Linux's EPOLL_CTL numbering (1 ADD, 2 DEL, 3 MOD). The SCE values are 1 ADD, 2 MOD, 3 DEL (shadPS4 net_types.h, ORBIS_NET_EPOLL_CTL_*). A title removing a socket with op 3 and a null event got EINVAL, and op 2 deleted the registration it meant to modify. PPSA01289 (Sackboy: A Big Adventure) removes its UDP socket from its epoll this way before closing it, every time its network session is torn down. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceNet/Export.cpp | 13 ++++++++----- core/libs/tests/GuestSceNet.cpp | 6 ++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceNet/Export.cpp b/core/libs/prx/libSceNet/Export.cpp index 3bfaa1229..06ce53847 100644 --- a/core/libs/prx/libSceNet/Export.cpp +++ b/core/libs/prx/libSceNet/Export.cpp @@ -70,6 +70,9 @@ constexpr int NET_AF_INET6 = 28; constexpr int NET_SOCK_STREAM = 1; constexpr int NET_SOCK_DGRAM = 2; constexpr int NET_SOCK_RAW = 3; +constexpr int NET_EPOLL_CTL_ADD = 1; +constexpr int NET_EPOLL_CTL_MOD = 2; +constexpr int NET_EPOLL_CTL_DEL = 3; constexpr int NET_SOL_SOCKET = 0xFFFF; constexpr int NET_SO_SNDTIMEO = 0x1005; constexpr int NET_SO_RCVTIMEO = 0x1006; @@ -958,13 +961,13 @@ int APS5_VABI sceNetEpollControl(int eid, int op, int id, const NetEpollEvent* e if (g_epolls.count(eid) == 0) { return fail(NET_EBADF); } - if (op < 1 || op > 3) return fail(NET_EINVAL); - if (op != 2 && !event) return fail(NET_EINVAL); - if (op != 2 && g_socks.count(id) == 0) return fail(NET_EBADF); + if (op != NET_EPOLL_CTL_ADD && op != NET_EPOLL_CTL_MOD && op != NET_EPOLL_CTL_DEL) return fail(NET_EINVAL); + if (op != NET_EPOLL_CTL_DEL && !event) return fail(NET_EINVAL); + if (op != NET_EPOLL_CTL_DEL && g_socks.count(id) == 0) return fail(NET_EBADF); auto& registrations = g_epoll_socks[eid]; - if (op == 1) { + if (op == NET_EPOLL_CTL_ADD) { if (!registrations.emplace(id, *event).second) return fail(NET_EADDRINUSE); - } else if (op == 2) { + } else if (op == NET_EPOLL_CTL_DEL) { if (registrations.erase(id) == 0) return fail(NET_ENOENT); } else { auto registration = registrations.find(id); diff --git a/core/libs/tests/GuestSceNet.cpp b/core/libs/tests/GuestSceNet.cpp index 1579e6645..6c82f8ea1 100644 --- a/core/libs/tests/GuestSceNet.cpp +++ b/core/libs/tests/GuestSceNet.cpp @@ -400,6 +400,12 @@ int main() { Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1 && (ready.events & 1) && ready.ident == static_cast(accepted)); Require(sceNetShutdown(accepted, 1) == 0); Require(Failed(sceNetSend(accepted, request, sizeof(request), 0), 32)); + registration.data.u32 = 78; + Require(sceNetEpollControl(epoll, 2, accepted, ®istration) == 0); + Require(Failed(sceNetEpollControl(epoll, 2, accepted, nullptr), 22)); + Require(sceNetEpollControl(epoll, 3, accepted, nullptr) == 0); + Require(Failed(sceNetEpollControl(epoll, 3, accepted, nullptr), 2)); + Require(Failed(sceNetEpollControl(epoll, 2, accepted, ®istration), 2)); Require(sceNetEpollDestroy(epoll) == 0); Require(sceNetSocketClose(accepted) == 0); bool send_failed = false;