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 <noreply@anthropic.com>
This commit is contained in:
jdmagic 2026-10-09 13:50:06 -04:00
parent bab7b32852
commit 22827bf6ad
2 changed files with 14 additions and 5 deletions

View File

@ -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);

View File

@ -400,6 +400,12 @@ int main() {
Require(sceNetEpollWait(epoll, &ready, 1, 1000000) == 1 && (ready.events & 1) && ready.ident == static_cast<std::uint64_t>(accepted));
Require(sceNetShutdown(accepted, 1) == 0);
Require(Failed(sceNetSend(accepted, request, sizeof(request), 0), 32));
registration.data.u32 = 78;
Require(sceNetEpollControl(epoll, 2, accepted, &registration) == 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, &registration), 2));
Require(sceNetEpollDestroy(epoll) == 0);
Require(sceNetSocketClose(accepted) == 0);
bool send_failed = false;