From cba3b5a8b38ce972d03f700f108e99175f4d8ce4 Mon Sep 17 00:00:00 2001 From: milescoviello Date: Sun, 11 Oct 2026 10:30:44 -0700 Subject: [PATCH] fix(libkernel): accept SOCK_NONBLOCK and SOCK_CLOEXEC in socket socket only took a type of exactly SOCK_STREAM or SOCK_DGRAM, so a type with SOCK_NONBLOCK (0x20000000) or SOCK_CLOEXEC (0x10000000) set failed with EPROTONOSUPPORT, although socketpair already accepted both. FreeBSD strips both flags before creating the socket (sys/kern/ uipc_syscalls.c, kern_socket: "if ((type & SOCK_CLOEXEC) != 0) { type &= ~SOCK_CLOEXEC; oflag |= O_CLOEXEC; }" and the same for SOCK_NONBLOCK with FNONBLOCK), and makes the new socket non-blocking with "fo_ioctl(fp, FIONBIO, &fflag, ...)". socket now does the same as socketpair: it creates the socket from the plain type and, with SOCK_NONBLOCK, switches it to non-blocking. SOCK_CLOEXEC needs nothing more, since a guest never executes another program. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 11 +++++++---- core/libs/tests/GuestUdp.cpp | 10 ++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 52f37a428..55e2a03dc 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -487,9 +487,11 @@ int APS5_VABI getsockopt_nid_postfix(int descriptor, int level, int option, return 0; } int APS5_VABI socket_nid_postfix(int family, int type, int protocol) { + const bool nonblocking = (type & GuestNonBlocking) != 0; + const int plainType = type & ~(GuestCloseOnExec | GuestNonBlocking); if (family != 2 && family != 28) return Fail(47); - const int nativeType = type == 1 ? SOCK_STREAM : type == 2 ? SOCK_DGRAM : -1; - const int expectedProtocol = type == 1 ? 6 : 17; + const int nativeType = plainType == 1 ? SOCK_STREAM : plainType == 2 ? SOCK_DGRAM : -1; + const int expectedProtocol = plainType == 1 ? 6 : 17; if (nativeType == -1 || (protocol != 0 && protocol != expectedProtocol)) return Fail(43); #ifdef _WIN32 static const int startup = [] { WSADATA data{}; return WSAStartup(MAKEWORD(2, 2), &data); }(); @@ -497,10 +499,11 @@ int APS5_VABI socket_nid_postfix(int family, int type, int protocol) { #endif const auto native = ::socket(family == 2 ? AF_INET : AF_INET6, nativeType, protocol); if (native == Invalid) return Fail(NativeError()); - Socket guard(native, family, type); + Socket guard(native, family, plainType); try { - auto socket = std::make_shared(native, family, type); + auto socket = std::make_shared(native, family, plainType); guard.value = Invalid; + if (nonblocking && SetNonBlocking(*socket)) return -1; std::lock_guard lock(socketsMutex); if (nextDescriptor == INT_MAX) return Fail(24); const int descriptor = nextDescriptor++; diff --git a/core/libs/tests/GuestUdp.cpp b/core/libs/tests/GuestUdp.cpp index 2f69a2201..69386d2db 100644 --- a/core/libs/tests/GuestUdp.cpp +++ b/core/libs/tests/GuestUdp.cpp @@ -163,4 +163,14 @@ int main() { const int stream = socket_nid_postfix(2, 1, 0); Require(stream >= 0); Require(close_nid_postfix(stream) == 0); + const int nonblocking = socket_nid_postfix(2, 2 | 0x20000000, 0); + Require(nonblocking >= 0 && fcntl_nid_postfix(nonblocking, 3) == 6); + std::array nonblockingAddress{16, 2, 0, 0, 127, 0, 0, 1}; + Require(bind_nid_postfix(nonblocking, nonblockingAddress.data(), nonblockingAddress.size()) == 0); + Require(recvfrom_nid_postfix(nonblocking, buffer, sizeof(buffer), 0, nullptr, nullptr) == -1 && *__error_nid_postfix() == 35); + Require(close_nid_postfix(nonblocking) == 0); + const int closeOnExec = socket_nid_postfix(2, 1 | 0x10000000, 6); + Require(closeOnExec >= 0 && fcntl_nid_postfix(closeOnExec, 3) == 2); + Require(close_nid_postfix(closeOnExec) == 0); + Require(socket_nid_postfix(2, 0x20000000, 0) == -1 && *__error_nid_postfix() == 43); }