diff --git a/src/libs/network.cpp b/src/libs/network.cpp index cfc9ee82..0198e6f7 100644 --- a/src/libs/network.cpp +++ b/src/libs/network.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #endif @@ -30,6 +31,7 @@ #include "libs/libs.h" #include "libs/network.h" +#include #include #include #include @@ -37,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -832,10 +835,12 @@ struct NetEtherAddr { using NativeSocket = SOCKET; static constexpr NativeSocket INVALID_NATIVE_SOCKET = INVALID_SOCKET; using SocketLength = int; +using SocketIoLength = int; #else using NativeSocket = int; static constexpr NativeSocket INVALID_NATIVE_SOCKET = -1; using SocketLength = socklen_t; +using SocketIoLength = size_t; #endif struct SocketSlot { @@ -953,6 +958,7 @@ static int SetHostSocketError(int error) { case EADDRINUSE: posix_error = Posix::POSIX_EADDRINUSE; break; case EADDRNOTAVAIL: posix_error = Posix::POSIX_EADDRNOTAVAIL; break; case EAFNOSUPPORT: posix_error = Posix::POSIX_EAFNOSUPPORT; break; + case EAGAIN: posix_error = Posix::POSIX_EWOULDBLOCK; break; case EBADF: posix_error = Posix::POSIX_EBADF; break; case EFAULT: posix_error = Posix::POSIX_EFAULT; break; case EINVAL: posix_error = Posix::POSIX_EINVAL; break; @@ -989,7 +995,6 @@ static int ConvertSocketOptionLevel(int level) { } static int ConvertMessageFlags(int flags) { -#if defined(_WIN32) constexpr int guest_msg_peek = 0x00000002; constexpr int guest_msg_dontroute = 0x00000004; constexpr int guest_msg_waitall = 0x00000040; @@ -1006,6 +1011,14 @@ static int ConvertMessageFlags(int flags) { if ((flags & guest_msg_waitall) != 0) { host_flags |= MSG_WAITALL; } +#if !defined(_WIN32) + if ((flags & guest_msg_dontwait) != 0) { + host_flags |= MSG_DONTWAIT; + } + if ((flags & guest_msg_nosignal) != 0) { + host_flags |= MSG_NOSIGNAL; + } +#endif flags &= ~(guest_msg_peek | guest_msg_dontroute | guest_msg_waitall | guest_msg_dontwait | guest_msg_nosignal); @@ -1015,9 +1028,6 @@ static int ConvertMessageFlags(int flags) { } return host_flags; -#else - return flags; -#endif } static int ConvertGuestSockaddr(const void* addr, uint32_t addrlen, sockaddr_storage* out, @@ -1942,34 +1952,30 @@ int64_t KYTY_SYSV_ABI Sendto(int s, const void* buf, uint64_t len, int flags, co return -1; } -#if defined(_WIN32) const int host_flags = ConvertMessageFlags(flags); if (host_flags < 0) { return -1; } - const int host_len = static_cast(len > 0x7fffffffu ? 0x7fffffffu : len); - int result = 0; + const auto host_len = static_cast( + std::min(len, std::numeric_limits::max())); + int64_t result = 0; if (addr == nullptr) { result = ::send(socket, static_cast(buf), host_len, host_flags); } else { sockaddr_storage host_addr {}; - int host_addrlen = 0; + SocketLength host_addrlen = 0; if (ConvertGuestSockaddr(addr, addrlen, &host_addr, &host_addrlen) != 0) { return -1; } result = ::sendto(socket, static_cast(buf), host_len, host_flags, reinterpret_cast(&host_addr), host_addrlen); } - if (result == SOCKET_ERROR) { + if (result < 0) { return SetHostSocketError(); } return result; -#else - *Posix::GetErrorAddr() = Posix::POSIX_ENOSYS; - return -1; -#endif } int64_t KYTY_SYSV_ABI Recv(int s, void* buf, uint64_t len, int flags) { @@ -2000,35 +2006,31 @@ int64_t KYTY_SYSV_ABI Recvfrom(int s, void* buf, uint64_t len, int flags, void* return -1; } -#if defined(_WIN32) const int host_flags = ConvertMessageFlags(flags); if (host_flags < 0) { return -1; } - const int host_len = static_cast(len > 0x7fffffffu ? 0x7fffffffu : len); - int result = 0; + const auto host_len = static_cast( + std::min(len, std::numeric_limits::max())); + int64_t result = 0; if (addr == nullptr) { result = ::recv(socket, static_cast(buf), host_len, host_flags); } else { sockaddr_storage host_addr {}; - int host_addrlen = sizeof(host_addr); + SocketLength host_addrlen = sizeof(host_addr); result = ::recvfrom(socket, static_cast(buf), host_len, host_flags, reinterpret_cast(&host_addr), &host_addrlen); - if (result != SOCKET_ERROR && + if (result >= 0 && ConvertHostSockaddr(&host_addr, host_addrlen, addr, addrlen) != 0) { return -1; } } - if (result == SOCKET_ERROR) { + if (result < 0) { return SetHostSocketError(); } return result; -#else - *Posix::GetErrorAddr() = Posix::POSIX_ENOSYS; - return -1; -#endif } int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfds, @@ -2062,7 +2064,6 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd LOGF("\n"); } -#if defined(_WIN32) fd_set host_read {}; fd_set host_write {}; fd_set host_except {}; @@ -2070,91 +2071,92 @@ int KYTY_SYSV_ABI Select(int nfds, void* readfds, void* writefds, void* exceptfd FD_ZERO(&host_write); FD_ZERO(&host_except); - std::array read_map {}; - std::array write_map {}; - std::array except_map {}; - int read_count = 0; - int write_count = 0; - int except_count = 0; - + std::vector> descriptors; + int host_nfds = 0; for (int fd = 0; fd < nfds; fd++) { - NativeSocket socket = INVALID_NATIVE_SOCKET; - if (!GetSocketBackend(fd, &socket)) { + const bool read = GuestFdIsSet(readfds, fd); + const bool write = GuestFdIsSet(writefds, fd); + const bool except = GuestFdIsSet(exceptfds, fd); + if (!read && !write && !except) { continue; } - if (GuestFdIsSet(readfds, fd) && read_count < FD_SETSIZE) { + NativeSocket socket = INVALID_NATIVE_SOCKET; +#if !defined(_WIN32) + if (fd < 3) { + socket = fd; + } else +#endif + if (!GetSocketBackend(fd, &socket)) { + return SetGuestSocketError(Posix::POSIX_EBADF); + } +#if defined(_WIN32) + if ((read && host_read.fd_count == FD_SETSIZE) || + (write && host_write.fd_count == FD_SETSIZE) || + (except && host_except.fd_count == FD_SETSIZE)) { + return SetGuestSocketError(Posix::POSIX_EINVAL); + } +#else + if (socket >= FD_SETSIZE) { + return SetGuestSocketError(Posix::POSIX_EINVAL); + } + host_nfds = std::max(host_nfds, socket + 1); +#endif + descriptors.emplace_back(fd, socket); + if (read) { FD_SET(socket, &host_read); - read_map[static_cast(read_count++)] = fd; } - if (GuestFdIsSet(writefds, fd) && write_count < FD_SETSIZE) { + if (write) { FD_SET(socket, &host_write); - write_map[static_cast(write_count++)] = fd; } - if (GuestFdIsSet(exceptfds, fd) && except_count < FD_SETSIZE) { + if (except) { FD_SET(socket, &host_except); - except_map[static_cast(except_count++)] = fd; } } - timeval host_timeout {}; + timeval host_timeout {}; timeval* host_timeout_ptr = nullptr; if (timeout != nullptr) { const auto* guest_timeout = static_cast(timeout); - host_timeout.tv_sec = static_cast(guest_timeout->tv_sec); - host_timeout.tv_usec = static_cast(guest_timeout->tv_usec); - host_timeout_ptr = &host_timeout; - } - - fd_set* read_ptr = (read_count != 0 ? &host_read : nullptr); - fd_set* write_ptr = (write_count != 0 ? &host_write : nullptr); - fd_set* except_ptr = (except_count != 0 ? &host_except : nullptr); - if (read_ptr == nullptr && write_ptr == nullptr && except_ptr == nullptr) { - if (host_timeout_ptr != nullptr) { - const auto sleep_ms = - static_cast(host_timeout.tv_sec * 1000 + host_timeout.tv_usec / 1000); - Sleep(sleep_ms); + if (guest_timeout->tv_sec < 0 || guest_timeout->tv_usec < 0 || + guest_timeout->tv_usec >= 1'000'000) { + return SetGuestSocketError(Posix::POSIX_EINVAL); } - GuestFdZero(readfds, nfds); - GuestFdZero(writefds, nfds); - GuestFdZero(exceptfds, nfds); - return 0; + host_timeout.tv_sec = static_cast(guest_timeout->tv_sec); + host_timeout.tv_usec = static_cast(guest_timeout->tv_usec); + host_timeout_ptr = &host_timeout; } - const int result = ::select(0, read_ptr, write_ptr, except_ptr, host_timeout_ptr); - if (result == SOCKET_ERROR) { - return SetHostSocketError(); + int result = 0; +#if defined(_WIN32) + if (descriptors.empty()) { + Sleep(host_timeout_ptr == nullptr ? INFINITE : + static_cast(host_timeout.tv_sec * 1000 + host_timeout.tv_usec / 1000)); + } else +#endif + { + result = ::select(host_nfds, readfds != nullptr ? &host_read : nullptr, + writefds != nullptr ? &host_write : nullptr, + exceptfds != nullptr ? &host_except : nullptr, host_timeout_ptr); + if (result < 0) { + return SetHostSocketError(); + } } GuestFdZero(readfds, nfds); GuestFdZero(writefds, nfds); GuestFdZero(exceptfds, nfds); - for (int i = 0; i < read_count; i++) { - NativeSocket socket = INVALID_NATIVE_SOCKET; - if (GetSocketBackend(read_map[static_cast(i)], &socket) && - FD_ISSET(socket, &host_read)) { - GuestFdSet(readfds, read_map[static_cast(i)]); + for (const auto& [fd, socket]: descriptors) { + if (FD_ISSET(socket, &host_read)) { + GuestFdSet(readfds, fd); + } + if (FD_ISSET(socket, &host_write)) { + GuestFdSet(writefds, fd); + } + if (FD_ISSET(socket, &host_except)) { + GuestFdSet(exceptfds, fd); } } - for (int i = 0; i < write_count; i++) { - NativeSocket socket = INVALID_NATIVE_SOCKET; - if (GetSocketBackend(write_map[static_cast(i)], &socket) && - FD_ISSET(socket, &host_write)) { - GuestFdSet(writefds, write_map[static_cast(i)]); - } - } - for (int i = 0; i < except_count; i++) { - NativeSocket socket = INVALID_NATIVE_SOCKET; - if (GetSocketBackend(except_map[static_cast(i)], &socket) && - FD_ISSET(socket, &host_except)) { - GuestFdSet(exceptfds, except_map[static_cast(i)]); - } - } - return result; -#else - *Posix::GetErrorAddr() = Posix::POSIX_ENOSYS; - return -1; -#endif } } // namespace Net diff --git a/tests/KernelFileSystemTests.cpp b/tests/KernelFileSystemTests.cpp index 28294496..da26ee42 100644 --- a/tests/KernelFileSystemTests.cpp +++ b/tests/KernelFileSystemTests.cpp @@ -5,6 +5,9 @@ #include "common/threads.h" #include "kernel/fileSystem.h" #include "libs/errno.h" +#include "libs/network.h" + +#include #include #include @@ -76,6 +79,63 @@ void CheckSaveRename(const std::filesystem::path &root, "renamed save contents"); } +void CheckSocketWakeup() { + namespace Net = Libs::Network::Net; + // Guest sockaddr_in: length, family, network-order port/address, padding. + std::array address {16, 2, 0, 0, 127, 0, 0, 1}; + const int listener = Net::Socket(2, 1, 0); + Check(listener >= 0, "create loopback listener"); + Check(Net::Bind(listener, address.data(), address.size()) == 0, "bind loopback"); + Check(Net::Listen(listener, 1) == 0, "listen on loopback"); + uint32_t address_size = address.size(); + Check(Net::Getsockname(listener, address.data(), &address_size) == 0, + "get assigned loopback port"); + const int writer = Net::Socket(2, 1, 0); + Check(writer >= 0 && Net::Connect(writer, address.data(), address_size) == 0, + "connect wake socket"); + const int reader = Net::Accept(listener, nullptr, nullptr); + Check(reader >= 0, "accept wake socket"); + Check(Net::SocketClose(listener) == 0, "close listener"); + const int enabled = 1; + Check(Net::Setsockopt(writer, 6, 1, &enabled, sizeof(enabled)) == 0, + "enable TCP_NODELAY"); + + std::array readable {}; + const auto bit = uint64_t {1} << (reader % 64); + readable[reader / 64] = bit; + const std::array immediate {0, 0}; + Check(Net::Select(reader + 1, readable.data(), nullptr, nullptr, + immediate.data()) == 0 && readable[reader / 64] == 0, + "empty socket is not readable"); + const char payload[] = "wake"; + Check(Net::Send(writer, payload, sizeof(payload), 0x20000) == sizeof(payload), + "send wake bytes with guest MSG_NOSIGNAL"); + readable[reader / 64] = bit; + const std::array deadline {1, 0}; + Check(Net::Select(reader + 1, readable.data(), nullptr, nullptr, + deadline.data()) == 1 && readable[reader / 64] == bit, + "select reports the guest descriptor after wake"); + std::array received {}; + Check(Net::Recv(reader, received.data(), received.size(), 0x42) == sizeof(payload) && + std::memcmp(received.data(), payload, sizeof(payload)) == 0, + "guest PEEK and WAITALL preserve the wake bytes"); + Check(Net::Recv(reader, received.data(), received.size(), 0x40) == sizeof(payload), + "consume wake bytes with guest WAITALL"); +#if !defined(_WIN32) + Check(Net::Recv(reader, received.data(), received.size(), 0x80) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EWOULDBLOCK, + "empty nonblocking receive translates guest errno"); +#endif + Check(Net::SocketClose(reader) == 0 && Net::SocketClose(writer) == 0, + "close wake sockets"); + readable[reader / 64] = bit; + Check(Net::Select(reader + 1, readable.data(), nullptr, nullptr, + immediate.data()) == -1 && + *Libs::Posix::GetErrorAddr() == Libs::Posix::POSIX_EBADF && + readable[reader / 64] == bit, + "closed descriptor fails without clearing input fd_set"); +} + } // namespace int main() { @@ -93,6 +153,7 @@ int main() { CheckSaveRename(temporary.Path(), "first-save"); CheckSaveRename(temporary.Path(), "replacement-save"); FileSystem::Shutdown(); + CheckSocketWakeup(); subsystems.Destroy(); std::printf("KernelFileSystemTests: all cases passed\n");