fix(libkernel): give guest sockets ordinary descriptor numbers below FD_SETSIZE

libkernel numbered guest sockets from GuestSockets::FirstDescriptor
(0x10000000), so a title that puts a socket in a 1024-bit fd_set writes
fd / 8 bytes past it. The Epic Online Services SDK does this on its
RtcNetworkThread and three titles stop there (#1794):

Palworld PPSA23126 (queeeef, main a11c8f7f7):
  FATAL: unhandled exception 0xc0000005 on thread 37164 'RtcNetworkThread'
    rip 0x00007ffcdb1ea2be eossdk-ps5-shipping.prx.guest.prx+0xdea2be
    rcx 0000000010000005 rsi 0000000000400000 r12 0000000010000005
The Mound PPSA26223 (xXPBSXx, main 0a8b1d32b):
  FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread',
  rip eossdk-ps5-shipping.prx.guest.prx+0xefc6f4, rcx = 0x10000005,
  rsi = 0x400000
Dying Light 2 PPSA02262 (tv7):
  FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread'
    rcx 0000000010000005 ... rsi 0000000000400000 ... r12 0000000010000005

On FreeBSD 11.4 (8c639945) FD_SETSIZE is 1024 (sys/sys/select.h:58-59)
and sockets take the lowest free entry of the one process descriptor
table that files use (kern_descrip.c:1643-1660 fdalloc,
uipc_syscalls.c kern_socket through falloc).

Every socket number is now a real host descriptor: a zero-access NUL
handle registered with _open_osfhandle on Windows (as directory
descriptors already are) and an F_DUPFD_CLOEXEC duplicate of the host
socket on Linux. The socket table maps that number to the socket, so
"is this a socket" is a table lookup instead of a range check at the 31
former FirstDescriptor comparisons. close releases both the socket entry
and the number, dup and dup2 duplicate the number through the host table
and keep the socket shared, dup2 of a file onto a socket number drops the
socket entry, and sceKernelClose now closes sockets too. sceNetSocket and
sceNetAccept take their ids from the same table instead of a counter from
32, so libkernel and libSceNet numbers never collide.

select polls guest sockets alongside libSceNet sockets, the random device,
stdio and (on Linux) host descriptors; poll throws only for an open
descriptor that is not a guest socket and reports POLLNVAL otherwise.
File calls that used to fail with EBADF on the socket range return
FreeBSD's answer for a socket: lseek ESPIPE (kern_lseek), ftruncate and
getdirentries EINVAL (invfo_truncate, getvnode), flock EOPNOTSUPP;
fstat on a socket throws. pread on a closed descriptor returns EBADF.

title failing on host descriptor numbers; these three titles are that
case, and this change keeps host numbering for files and only moves
sockets into it.

Tests: guest_socket_descriptors (new: socket below 1024, select on a
socket and a file together, close frees the number for a file, dup of a
socket, sceKernelClose), guest_select (socket and pipe together, Linux),
guest_poll, guest_dup, guest_socket_pair, guest_fstatfs,
guest_posix_filesystem and guest_sce_net updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 20378375b31eb5db922db6e55f45e44a4dae6445)
This commit is contained in:
screamm authored and MarcZX100 committed 2026-10-13 03:22:36 +00:00
1 parent c13dc7e6b4
commit e99816e8f3
20 files changed
+584 -206

No files matched your search

+5 -3
View File
@@ -620,8 +620,9 @@ int APS5_VABI sceNetSocket(const char* name, int family, int type, int protocol)
close_socket(native);
return fail(12);
}
const int fd = KernelReserveSocketNumber_nid_no_patch(static_cast<std::uintptr_t>(handle->value));
if (fd < 0) return fail(-fd);
std::lock_guard lk(g_mutex);
const int fd = g_next_sock++;
Sock s;
s.family = family;
s.type = type;
@@ -639,6 +640,7 @@ int APS5_VABI sceNetSocketClose(int s) {
native = it->second.native;
g_socks.erase(it);
}
KernelReleaseSocketNumber_nid_no_patch(s);
if (native) {
#ifdef _WIN32
::shutdown(native->value, SD_BOTH);
@@ -735,10 +737,10 @@ int APS5_VABI sceNetAccept(int s, void* addr, uint32_t* addrlen) {
connection.family = listener.family;
connection.type = NET_SOCK_STREAM;
connection.native = std::move(handle);
int descriptor;
const int descriptor = KernelReserveSocketNumber_nid_no_patch(static_cast<std::uintptr_t>(connection.native->value));
if (descriptor < 0) return fail(-descriptor);
{
std::lock_guard lk(g_mutex);
descriptor = g_next_sock++;
g_socks.emplace(descriptor, connection);
}
if (addr && !native_to_guest_address(peer, addr, addrlen)) {
@@ -62,8 +62,8 @@ extern "C" int APS5_VABI mkstemp_nid_postfix(char* pattern) {
}
extern "C" int APS5_VABI isatty_nid_postfix(int descriptor) {
if (descriptor >= GuestSockets::FirstDescriptor) {
Failure(GuestSocketIsOpen_nid_no_patch(descriptor) ? 25 : 9);
if (GuestSocketIsOpen_nid_no_patch(descriptor)) {
Failure(25);
return 0;
}
#ifdef _WIN32
+10
View File
@@ -1043,6 +1043,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_kqueue PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_socket_descriptors_tests ${LIBS_INCLUDE_DIR}/tests/GuestSocketDescriptors.cpp)
target_include_directories(guest_socket_descriptors_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_socket_descriptors_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_socket_descriptors_tests)
add_test(NAME guest_socket_descriptors COMMAND guest_socket_descriptors_tests)
set_tests_properties(guest_socket_descriptors PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_socket_descriptors PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_socket_event_tests ${LIBS_INCLUDE_DIR}/tests/GuestSocketEvents.cpp)
target_include_directories(guest_socket_event_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_socket_event_tests PRIVATE libkernel libc)
+4 -2
View File
@@ -2,6 +2,7 @@
#include "prx/libkernel/Time/include/Time.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
#include "prx/libkernel/File/include/File.hpp"
#include <algorithm>
#include <chrono>
@@ -593,13 +594,14 @@ int APS5_VABI sceKernelDeleteAmprSystemEvent(KernelEqueue eq, int id) {
}
static int AddDescriptorEvent(KernelEqueue eq, int fd, std::size_t size, void* udata, int16_t filter) {
if (fd < GuestSockets::FirstDescriptor) {
const bool isSocket = GuestSockets::IsOpen(fd);
if (!isSocket && DescriptorIsOpen_nid_no_patch(fd)) {
NotImplemented_nid_no_patch(filter == EVFILT_READ ? "sceKernelAddReadEvent: non-socket descriptor" : "sceKernelAddWriteEvent: non-socket descriptor");
}
if (size > 1) {
NotImplemented_nid_no_patch(filter == EVFILT_READ ? "sceKernelAddReadEvent: low watermark" : "sceKernelAddWriteEvent: low watermark");
}
if (!GuestSockets::IsOpen(fd)) {
if (!isSocket) {
return SCE_KERNEL_ERROR_EBADF;
}
return EqueueAddEvent_nid_postfix(eq, EqueueDescriptorEvent(fd, filter, EV_ADD, udata));
+10 -6
View File
@@ -221,7 +221,7 @@ int OpenInDirectory_nid_no_patch(int directory, const char* path, int flags, std
}
int APS5_VABI sceKernelClose(int d) {
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Close(d) == 0 ? 0 : SceKernelError(*__error_nid_postfix());
if (GuestSockets::IsOpen(d)) return GuestSockets::Close(d) == 0 ? 0 : SceKernelError(*__error_nid_postfix());
EqueueDescriptorClosed(d);
File::ForgetRandomDevice(d);
File::ForgetDescriptor(d);
@@ -238,7 +238,7 @@ int APS5_VABI sceKernelClose(int d) {
}
std::int64_t APS5_VABI sceKernelRead(int d, void* buf, std::size_t nbytes) {
if (d >= GuestSockets::FirstDescriptor) {
if (GuestSockets::IsOpen(d)) {
const auto n = GuestSockets::Read(d, buf, nbytes);
return n < 0 ? SceKernelError(*__error_nid_postfix()) : n;
}
@@ -254,7 +254,7 @@ std::int64_t APS5_VABI sceKernelRead(int d, void* buf, std::size_t nbytes) {
}
std::int64_t APS5_VABI sceKernelWrite(int d, const void* buf, std::size_t nbytes) {
if (d >= GuestSockets::FirstDescriptor) {
if (GuestSockets::IsOpen(d)) {
const auto n = GuestSockets::Write(d, buf, nbytes);
return n < 0 ? SceKernelError(*__error_nid_postfix()) : n;
}
@@ -267,6 +267,7 @@ std::int64_t APS5_VABI sceKernelWrite(int d, const void* buf, std::size_t nbytes
}
std::int64_t APS5_VABI sceKernelLseek(int d, std::int64_t offset, int whence) {
if (GuestSockets::IsOpen(d)) return SCE_KERNEL_ERROR_ESPIPE;
if (whence < 0 || whence > 2) {
throw std::invalid_argument(std::string(__func__) + ": invalid whence=" + std::to_string(whence));
}
@@ -340,9 +341,12 @@ int APS5_VABI sceKernelFcntl(int d, int command, ...) {
#endif
}
int result;
if (d < GuestSockets::FirstDescriptor) result = File::Fcntl(d, command, argument);
else if (command == GuestSetFlags) result = fcntl_nid_postfix(d, command, static_cast<int>(argument));
else result = fcntl_nid_postfix(d, command);
if (GuestSockets::IsOpen(d)) {
if (command == GuestSetFlags) result = fcntl_nid_postfix(d, command, static_cast<int>(argument));
else result = fcntl_nid_postfix(d, command);
} else {
result = File::Fcntl(d, command, argument);
}
return result < 0 ? SceKernelError(*__error_nid_postfix()) : result;
}
+38 -29
View File
@@ -302,17 +302,17 @@ static constexpr int GUEST_EDEADLK = 11;
static constexpr int GUEST_ELOOP = 62;
static constexpr int GUEST_EDQUOT = 69;
static constexpr int GUEST_EOVERFLOW = 84;
static constexpr int GUEST_EOPNOTSUPP = 45;
static int SceErrorFromErrno(int error);
static std::int64_t PositionedGuestSocketError(int descriptor) {
if (descriptor < GuestSockets::FirstDescriptor) return 0;
return SceErrorFromErrno(GuestSockets::IsOpen(descriptor) ? GUEST_ESPIPE : GUEST_EBADF);
return GuestSockets::IsOpen(descriptor) ? SceErrorFromErrno(GUEST_ESPIPE) : 0;
}
static bool FailPosixPositionedGuestSocket(int descriptor) {
if (descriptor < GuestSockets::FirstDescriptor) return false;
errno = GuestSockets::IsOpen(descriptor) ? GUEST_ESPIPE : GUEST_EBADF;
if (!GuestSockets::IsOpen(descriptor)) return false;
errno = GUEST_ESPIPE;
return true;
}
@@ -491,13 +491,13 @@ int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
}
int APS5_VABI close_nid_postfix(int d) {
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Close(d);
int socketError = 0;
if (KernelCloseSocket_nid_no_patch(d, &socketError)) {
if (socketError == 0) return 0;
*__error_nid_postfix() = socketError;
return -1;
}
if (GuestSockets::IsOpen(d)) return GuestSockets::Close(d);
EqueueDescriptorClosed(d);
File::ForgetRandomDevice(d);
File::ForgetDescriptor(d);
@@ -544,7 +544,7 @@ int File::HostStandardError() {
extern "C" {
int APS5_VABI dup_nid_postfix(int d) {
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Duplicate(d);
if (GuestSockets::IsOpen(d)) return GuestSockets::Duplicate(d);
if (d < 0) return PosixFailure(GUEST_EBADF);
if (IsKqueueDescriptor(d)) throw std::runtime_error(std::string(__func__) + ": duplicating a kqueue descriptor is not supported");
#ifdef _WIN32
@@ -554,8 +554,6 @@ int APS5_VABI dup_nid_postfix(int d) {
const int duplicate = ::dup(d);
#endif
if (duplicate < 0) return PosixFailure(SceErrorFromErrno(errno) & 0xffff);
if (duplicate >= GuestSockets::FirstDescriptor)
throw std::runtime_error(std::string(__func__) + ": host descriptor reached the guest socket range");
if (File::IsRandomDevice(d)) File::RememberRandomDevice(duplicate);
File::ShareDescriptor(d, duplicate);
DuplicateJitSharedMemory(d, duplicate);
@@ -564,15 +562,18 @@ int APS5_VABI dup_nid_postfix(int d) {
int APS5_VABI dup2_nid_postfix(int from, int to) {
if (from < 0 || to < 0) return PosixFailure(GUEST_EBADF);
const bool socketFrom = from >= GuestSockets::FirstDescriptor;
const bool socketTo = to >= GuestSockets::FirstDescriptor;
if (socketFrom && socketTo) return GuestSockets::DuplicateTo(from, to);
if (socketFrom) {
if (!GuestSockets::IsOpen(from)) return PosixFailure(GUEST_EBADF);
throw std::runtime_error(std::string(__func__) + ": moving a guest socket onto host descriptor " + std::to_string(to) + " is not supported");
if (GuestSockets::IsOpen(from)) {
if (from != to && to <= 2)
throw std::runtime_error(std::string(__func__) + ": moving a guest socket onto standard descriptor " + std::to_string(to) + " is not supported");
if (GuestSockets::DuplicateTo(from, to) < 0) return -1;
if (from == to) return to;
File::ForgetRandomDevice(to);
#ifdef _WIN32
File::ForgetDirectoryDescriptor(to);
File::ForgetFileLock(to);
#endif
return to;
}
if (socketTo)
throw std::runtime_error(std::string(__func__) + ": moving host descriptor " + std::to_string(from) + " into the guest socket range is not supported");
const bool sourceIsRandom = File::IsRandomDevice(from);
if (from != to && IsKqueueDescriptor(from)) throw std::runtime_error(std::string(__func__) + ": duplicating a kqueue descriptor is not supported");
#ifdef _WIN32
@@ -581,6 +582,7 @@ int APS5_VABI dup2_nid_postfix(int from, int to) {
if (to == 2) PreserveHostStandardError();
if (WithoutParameterHandler([from, to] { return ::_dup2(from, to); }) != 0) return PosixFailure(SceErrorFromErrno(errno) & 0xffff);
EqueueDescriptorClosed(to);
GuestSockets::Forget(to);
File::ForgetDirectoryDescriptor(to);
File::DuplicateDirectoryDescriptor(from, to);
File::ForgetFileLock(to);
@@ -598,6 +600,7 @@ int APS5_VABI dup2_nid_postfix(int from, int to) {
const int result = ::dup2(from, to);
if (result < 0) return PosixFailure(SceErrorFromErrno(errno) & 0xffff);
EqueueDescriptorClosed(to);
GuestSockets::Forget(to);
File::ForgetRandomDevice(to);
if (sourceIsRandom) File::RememberRandomDevice(to);
File::ShareDescriptor(from, to);
@@ -607,6 +610,7 @@ int APS5_VABI dup2_nid_postfix(int from, int to) {
}
int APS5_VABI flock_nid_postfix(int d, int operation) {
if (GuestSockets::IsOpen(d)) return PosixFailure(GUEST_EOPNOTSUPP);
const int type = operation & 8 ? 8 : operation & 2 ? 2 : operation & 1 ? 1 : 0;
if (type == 0) return PosixFailure(GUEST_EBADF);
if (NativeFlock(d, type | (operation & 4)) != 0) {
@@ -624,7 +628,7 @@ int APS5_VABI flock_nid_postfix(int d, int operation) {
}
int APS5_VABI _fstatfs_nid_postfix(int d, KernelStatfs* buf) {
if (d >= GuestSockets::FirstDescriptor) return PosixFailure(GuestSockets::IsOpen(d) ? GUEST_EINVAL : GUEST_EBADF);
if (GuestSockets::IsOpen(d)) return PosixFailure(GUEST_EINVAL);
if (d < 0) return PosixFailure(GUEST_EBADF);
KernelStatfs result{};
result.f_version = KERNEL_STATFS_VERSION;
@@ -640,12 +644,13 @@ int64_t APS5_VABI fstat_nid_disambig1_nid_postfix(int d, FileStat* sb) {
if (sb == nullptr) {
APS5_INVALID_ARG_EX;
}
if (GuestSockets::IsOpen(d)) throw std::runtime_error(std::string(__func__) + ": the status of a guest socket is not implemented");
File::FillFileStat(d, sb);
return 0;
}
int APS5_VABI ftruncate_nid_postfix(int d, int64_t length) {
if (length < 0) {
if (length < 0 || GuestSockets::IsOpen(d)) {
return PosixFailure(GUEST_EINVAL);
}
#ifdef _WIN32
@@ -723,6 +728,7 @@ int64_t APS5_VABI pread_nid_postfix(int d, void* buf, size_t nbytes, int64_t off
if (!destination.Open()) errno = EFAULT;
else if (File::ReadRandomDevice(d, buf, nbytes)) return static_cast<int64_t>(nbytes);
auto n = destination.Open() ? NativePread(d, buf, nbytes, offset) : -1;
if (n < 0 && errno == EBADF) return PosixFailure(GUEST_EBADF);
if (n < 0) {
throw std::runtime_error(std::string(__func__) + ": pread failed, fd=" + std::to_string(d) + ", errno=" + std::to_string(errno));
}
@@ -739,14 +745,14 @@ int64_t APS5_VABI pwrite_nid_disambig1_nid_postfix(int d, const void* buf, size_
}
int64_t APS5_VABI read_nid_postfix(int d, void* buf, uint64_t nbytes) {
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Read(d, buf, static_cast<size_t>(nbytes));
if (GuestSockets::IsOpen(d)) return GuestSockets::Read(d, buf, static_cast<size_t>(nbytes));
if (buf == nullptr && nbytes != 0) return PosixFailure(GUEST_EFAULT);
char emptyBuffer = 0;
return PosixResult(sceKernelRead(d, buf == nullptr ? &emptyBuffer : buf, static_cast<size_t>(nbytes)));
}
std::int64_t APS5_VABI _read_nid_postfix(int descriptor, void* buffer, std::size_t count) {
if (descriptor >= GuestSockets::FirstDescriptor) return GuestSockets::Read(descriptor, buffer, count);
if (GuestSockets::IsOpen(descriptor)) return GuestSockets::Read(descriptor, buffer, count);
if (buffer == nullptr && count != 0) return PosixFailure(GUEST_EFAULT);
char emptyBuffer = 0;
return PosixResult(sceKernelRead(descriptor, buffer == nullptr ? &emptyBuffer : buffer, count));
@@ -756,14 +762,14 @@ int64_t APS5_VABI write_nid_postfix(int d, const char* str, int64_t size) {
if (size < 0) {
APS5_INVALID_ARG_EX;
}
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Write(d, str, static_cast<std::size_t>(size));
if (GuestSockets::IsOpen(d)) return GuestSockets::Write(d, str, static_cast<std::size_t>(size));
if (str == nullptr && size != 0) return PosixFailure(GUEST_EFAULT);
const char emptyBuffer = 0;
return PosixResult(sceKernelWrite(d, str == nullptr ? &emptyBuffer : str, static_cast<size_t>(size)));
}
std::int64_t APS5_VABI _write_nid_postfix(int descriptor, const void* buffer, std::size_t count) {
if (descriptor >= GuestSockets::FirstDescriptor) return GuestSockets::Write(descriptor, buffer, count);
if (GuestSockets::IsOpen(descriptor)) return GuestSockets::Write(descriptor, buffer, count);
if (buffer == nullptr && count != 0) return PosixFailure(GUEST_EFAULT);
const char emptyBuffer = 0;
return PosixResult(sceKernelWrite(descriptor, buffer == nullptr ? &emptyBuffer : buffer, count));
@@ -801,12 +807,13 @@ int APS5_VABI sceKernelCheckReachability(const char* path) {
int APS5_VABI sceKernelFstat(int d, FileStat* sb) {
if (sb == nullptr) throw std::invalid_argument("sceKernelFstat: sb is null");
if (GuestSockets::IsOpen(d)) throw std::runtime_error("sceKernelFstat: the status of a guest socket is not implemented");
if (!File::FillFileStatFromDescriptor(d, sb)) return SceErrorFromErrno(errno);
return 0;
}
int APS5_VABI sceKernelFsync(int fd) {
if (fd >= GuestSockets::FirstDescriptor) return SceErrorFromErrno(GuestSockets::IsOpen(fd) ? GUEST_EINVAL : GUEST_EBADF);
if (GuestSockets::IsOpen(fd)) return SceErrorFromErrno(GUEST_EINVAL);
#ifdef _WIN32
if (NativeSync(fd) != 0) return SceErrorFromErrno(errno);
#else
@@ -816,7 +823,7 @@ int APS5_VABI sceKernelFsync(int fd) {
}
int APS5_VABI sceKernelFdatasync(int fd) {
if (fd >= GuestSockets::FirstDescriptor) return SceErrorFromErrno(GuestSockets::IsOpen(fd) ? GUEST_EINVAL : GUEST_EBADF);
if (GuestSockets::IsOpen(fd)) return SceErrorFromErrno(GUEST_EINVAL);
#ifdef _WIN32
if (NativeSync(fd) != 0) return SceErrorFromErrno(errno);
#else
@@ -837,6 +844,7 @@ int APS5_VABI sceKernelWriteThrottlingStatus(std::uint64_t* status) {
#ifdef _WIN32
int APS5_VABI sceKernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep) {
if (GuestSockets::IsOpen(fd)) return SceErrorFromErrno(GUEST_EINVAL);
if (buf == nullptr) return SceErrorFromErrno(GUEST_EFAULT);
if (nbytes <= 0) return SceErrorFromErrno(GUEST_EINVAL);
if (basep != nullptr) *basep = 0;
@@ -850,6 +858,7 @@ int APS5_VABI sceKernelGetdents(int fd, char* buf, int nbytes) {
#else
int APS5_VABI sceKernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep) {
if (GuestSockets::IsOpen(fd)) return SceErrorFromErrno(GUEST_EINVAL);
constexpr std::size_t GuestHeaderBytes = 8;
constexpr std::size_t GuestMaxName = 255;
if (buf == nullptr) return SceErrorFromErrno(GUEST_EFAULT);
@@ -980,7 +989,7 @@ static std::int64_t TransferIovecs(int d, const KernelIovec* iov, int iovcnt, co
}
std::deque<GuestArena::HostWrite> destinations;
if (!write && !OpenIovecs(iov, iovcnt, destinations)) return SceErrorFromErrno(GUEST_EFAULT);
if (offset == nullptr && d >= GuestSockets::FirstDescriptor) {
if (offset == nullptr && GuestSockets::IsOpen(d)) {
const auto result = write ? GuestSockets::Writev(d, reinterpret_cast<const GuestSockets::Iovec*>(iov), iovcnt)
: GuestSockets::Readv(d, reinterpret_cast<const GuestSockets::Iovec*>(iov), iovcnt);
return result < 0 ? SceKernelError(*__error_nid_postfix()) : result;
@@ -1048,7 +1057,7 @@ int64_t APS5_VABI sceKernelReadv(int d, const KernelIovec* iov, int iovcnt) {
if (const int error = CheckIovecs(iov, iovcnt)) return error;
std::deque<GuestArena::HostWrite> destinations;
if (!OpenIovecs(iov, iovcnt, destinations)) return SceErrorFromErrno(GUEST_EFAULT);
if (d >= GuestSockets::FirstDescriptor) {
if (GuestSockets::IsOpen(d)) {
const auto result = GuestSockets::Readv(d, reinterpret_cast<const GuestSockets::Iovec*>(iov), iovcnt);
return result < 0 ? SceKernelError(*__error_nid_postfix()) : result;
}
@@ -1059,7 +1068,7 @@ int64_t APS5_VABI sceKernelReadv(int d, const KernelIovec* iov, int iovcnt) {
int64_t APS5_VABI sceKernelWritev(int d, const KernelIovec* iov, int iovcnt) {
if (const int error = CheckIovecs(iov, iovcnt)) return error;
if (d >= GuestSockets::FirstDescriptor) {
if (GuestSockets::IsOpen(d)) {
const auto result = GuestSockets::Writev(d, reinterpret_cast<const GuestSockets::Iovec*>(iov), iovcnt);
return result < 0 ? SceKernelError(*__error_nid_postfix()) : result;
}
@@ -1155,7 +1164,7 @@ int APS5_VABI sceKernelChmod_nid_postfix(const char* path, std::uint16_t mode) {
}
int APS5_VABI sceKernelFchmod(int d, std::uint16_t mode) {
if (d >= GuestSockets::FirstDescriptor) return SceErrorFromErrno(GuestSockets::IsOpen(d) ? GUEST_EINVAL : GUEST_EBADF);
if (GuestSockets::IsOpen(d)) return SceErrorFromErrno(GUEST_EINVAL);
if (NativeFchmod(d, mode & 07777) != 0) return SceErrorFromErrno(errno);
return 0;
}
@@ -1204,7 +1213,7 @@ int APS5_VABI utimes_nid_postfix(const char* path, const KernelTimeval* times) {
}
int APS5_VABI sceKernelFutimes(int d, const KernelTimeval* times) {
if (d >= GuestSockets::FirstDescriptor) return SceErrorFromErrno(GuestSockets::IsOpen(d) ? GUEST_EINVAL : GUEST_EBADF);
if (GuestSockets::IsOpen(d)) return SceErrorFromErrno(GUEST_EINVAL);
if (times != nullptr) {
for (int i = 0; i < 2; ++i) {
if (times[i].tv_usec < 0 || times[i].tv_usec >= 1000000) return SceErrorFromErrno(GUEST_EINVAL);
@@ -59,11 +59,8 @@ bool HostPipe(int descriptor) {
#endif
int TerminalError(int descriptor) {
if (descriptor >= GuestSockets::FirstDescriptor) {
const int family = GuestSockets::Family(descriptor);
if (family < 0) return BadDescriptor;
return family == InternetFamily ? AddressNotAvailable : OperationNotSupported;
}
const int family = GuestSockets::Family(descriptor);
if (family >= 0) return family == InternetFamily ? AddressNotAvailable : OperationNotSupported;
if (descriptor < 0 || !HostOpen(descriptor)) return BadDescriptor;
return NotTerminal;
}
@@ -25,3 +25,5 @@ extern "C" void KernelSetSocketCloser_nid_no_patch(KernelSocketPoll::Closer clos
extern "C" bool KernelCloseSocket_nid_no_patch(int descriptor, int* error);
extern "C" void KernelSetSocketOptionSetter_nid_no_patch(KernelSocketPoll::OptionSetter setter);
extern "C" bool KernelSetSocketOption_nid_no_patch(int descriptor, int level, int option, const void* value, std::uint32_t length, int* error);
extern "C" int KernelReserveSocketNumber_nid_no_patch(std::uintptr_t native);
extern "C" void KernelReleaseSocketNumber_nid_no_patch(int number);
@@ -2,8 +2,8 @@
#include <cstddef>
#include <cstdint>
#include <vector>
#include "prx/libkernel/Socket/include/SocketPoll.hpp"
namespace GuestSockets {
constexpr int FirstDescriptor = 0x10000000;
struct Iovec {
void* base;
std::size_t length;
@@ -11,11 +11,13 @@ struct Iovec {
int Duplicate(int descriptor);
int DuplicateTo(int descriptor, int target);
int Close(int descriptor);
void Forget(int descriptor);
bool IsOpen(int descriptor);
int Family(int descriptor);
std::int64_t Read(int descriptor, void* buffer, std::size_t length);
std::int64_t Write(int descriptor, const void* buffer, std::size_t length);
bool Ready(int descriptor, bool write, std::int64_t* data, bool* eof);
int Poll(KernelSocketPoll::Entry* entries, int count, int timeoutMilliseconds);
struct Interest {
int descriptor;
bool write;
+65 -90
View File
@@ -47,6 +47,16 @@ void ClearDescriptor(void* set, int descriptor) {
if (set != nullptr) static_cast<std::uint64_t*>(set)[descriptor / 64] &= ~(std::uint64_t{1} << (descriptor % 64));
}
struct PollGroup {
KernelSocketPoll::Poller poll;
std::vector<KernelSocketPoll::Entry> entries;
std::vector<std::size_t> slots;
};
int KernelSockets(KernelSocketPoll::Entry* entries, int count, int timeoutMilliseconds) {
return GuestSockets::Poll(entries, count, timeoutMilliseconds);
}
#ifndef _WIN32
short HostEventFlags(short events) {
@@ -65,20 +75,17 @@ short KernelEventFlags(short revents) {
return flags;
}
int HostPoll(KernelSocketPoll::Entry* descriptors, std::size_t count, int timeoutMilliseconds) {
std::vector<struct pollfd> native(count);
for (std::size_t index = 0; index < count; ++index) {
int HostPoll(KernelSocketPoll::Entry* descriptors, int count, int timeoutMilliseconds) {
std::vector<struct pollfd> native(static_cast<std::size_t>(count));
for (std::size_t index = 0; index < native.size(); ++index) {
native[index].fd = descriptors[index].descriptor;
native[index].events = descriptors[index].events;
native[index].events = HostEventFlags(descriptors[index].events);
native[index].revents = 0;
}
const int result = ::poll(native.data(), static_cast<nfds_t>(count), timeoutMilliseconds);
if (result < 0) return -1;
for (std::size_t index = 0; index < count; ++index) {
if ((native[index].revents & POLLNVAL) != 0) {
errno = EBADF;
return -1;
}
const int result = ::poll(native.data(), static_cast<nfds_t>(native.size()), timeoutMilliseconds);
if (result < 0) return -errno;
for (std::size_t index = 0; index < native.size(); ++index) {
if ((native[index].revents & POLLNVAL) != 0) return -EBADF;
descriptors[index].revents = KernelEventFlags(native[index].revents);
}
return result;
@@ -131,10 +138,8 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
*__error_nid_postfix() = GuestEinval;
return -1;
}
std::vector<KernelSocketPoll::Entry> sockets;
std::vector<std::size_t> socketSlots;
std::vector<KernelSocketPoll::Entry> hosts;
std::vector<std::size_t> hostSlots;
PollGroup kernel{&KernelSockets, {}, {}};
PollGroup net{nullptr, {}, {}};
std::vector<KernelSocketPoll::Entry> devices;
std::vector<KernelSocketPoll::Entry> requested;
for (int descriptor = 0; descriptor < nfds; ++descriptor) {
@@ -143,57 +148,50 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
if (DescriptorSet(writefds, descriptor)) events |= KernelSocketPoll::Writable;
if (DescriptorSet(exceptfds, descriptor)) events |= KernelSocketPoll::Urgent;
if (events == 0) continue;
if (File::IsRandomDevice(descriptor)) devices.push_back({descriptor, events, static_cast<short>(events & (KernelSocketPoll::Readable | KernelSocketPoll::Writable))});
if (GuestSockets::IsOpen(descriptor)) {
kernel.entries.push_back({descriptor, events, 0});
kernel.slots.push_back(requested.size());
requested.push_back({descriptor, events, 0});
} else if (File::IsRandomDevice(descriptor)) devices.push_back({descriptor, events, static_cast<short>(events & (KernelSocketPoll::Readable | KernelSocketPoll::Writable))});
else if (descriptor <= 2) devices.push_back({descriptor, events, static_cast<short>(descriptor == 0 ? 0 : events & KernelSocketPoll::Writable)});
else {
sockets.push_back({descriptor, events, 0});
socketSlots.push_back(requested.size());
net.entries.push_back({descriptor, events, 0});
net.slots.push_back(requested.size());
requested.push_back({descriptor, events, 0});
}
}
for (const auto& device : devices) requested.push_back(device);
const bool devicesReady = std::any_of(devices.begin(), devices.end(), [](const auto& entry) { return entry.revents != 0; });
const auto poller = g_socketPoller.load();
if (!sockets.empty()) {
if (poller == nullptr) {
for (std::size_t index = 0; index < sockets.size();) {
const int descriptor = sockets[index].descriptor;
std::vector<PollGroup> groups;
if (!kernel.entries.empty()) groups.push_back(std::move(kernel));
if (!net.entries.empty()) {
PollGroup hosts{nullptr, {}, {}};
#ifndef _WIN32
if (descriptor < GuestSockets::FirstDescriptor) {
hosts.push_back({descriptor, HostEventFlags(sockets[index].events), 0});
hostSlots.push_back(socketSlots[index]);
sockets.erase(sockets.begin() + static_cast<std::ptrdiff_t>(index));
socketSlots.erase(socketSlots.begin() + static_cast<std::ptrdiff_t>(index));
continue;
}
hosts.poll = &HostPoll;
#endif
UnknownDescriptor(descriptor, false);
}
} else {
for (auto& socket : sockets) socket.revents = 0;
const int probe = poller(sockets.data(), static_cast<int>(sockets.size()), 0);
net.poll = g_socketPoller.load();
if (net.poll != nullptr) {
for (auto& socket : net.entries) socket.revents = 0;
const int probe = net.poll(net.entries.data(), static_cast<int>(net.entries.size()), 0);
if (probe < 0) {
*__error_nid_postfix() = -probe;
return -1;
}
for (std::size_t index = 0; index < sockets.size();) {
if ((sockets[index].revents & KernelSocketPoll::Unknown) == 0) {
++index;
continue;
}
const int descriptor = sockets[index].descriptor;
#ifndef _WIN32
if (descriptor < GuestSockets::FirstDescriptor) {
hosts.push_back({descriptor, HostEventFlags(sockets[index].events), 0});
hostSlots.push_back(socketSlots[index]);
sockets.erase(sockets.begin() + static_cast<std::ptrdiff_t>(index));
socketSlots.erase(socketSlots.begin() + static_cast<std::ptrdiff_t>(index));
continue;
}
#endif
UnknownDescriptor(descriptor, true);
}
}
for (std::size_t index = 0; index < net.entries.size();) {
if (net.poll != nullptr && (net.entries[index].revents & KernelSocketPoll::Unknown) == 0) {
++index;
continue;
}
const int descriptor = net.entries[index].descriptor;
if (hosts.poll == nullptr) UnknownDescriptor(descriptor, net.poll != nullptr);
hosts.entries.push_back({descriptor, net.entries[index].events, 0});
hosts.slots.push_back(net.slots[index]);
net.entries.erase(net.entries.begin() + static_cast<std::ptrdiff_t>(index));
net.slots.erase(net.slots.begin() + static_cast<std::ptrdiff_t>(index));
}
if (!net.entries.empty()) groups.push_back(std::move(net));
if (!hosts.entries.empty()) groups.push_back(std::move(hosts));
}
const auto start = std::chrono::steady_clock::now();
const auto deadline = limit == nullptr ? std::chrono::steady_clock::time_point::max()
@@ -206,48 +204,25 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
waitMilliseconds = static_cast<int>(std::clamp<std::int64_t>(roundedMilliseconds, 0, 100));
}
if (devicesReady) waitMilliseconds = 0;
#ifndef _WIN32
if (!hosts.empty() && !sockets.empty()) {
if (poller == nullptr) UnknownDescriptor(sockets.front().descriptor, false);
if (groups.empty()) {
if (waitMilliseconds > 0) std::this_thread::sleep_for(std::chrono::milliseconds(waitMilliseconds));
} else {
const int slice = groups.size() == 1 ? waitMilliseconds : std::min(waitMilliseconds, SelectPollSliceMilliseconds);
const auto sliceEnd = std::chrono::steady_clock::now() + std::chrono::milliseconds(waitMilliseconds);
for (;;) {
for (auto& host : hosts) host.revents = 0;
const int hostReady = HostPoll(hosts.data(), hosts.size(), SelectPollSliceMilliseconds);
if (hostReady < 0) {
*__error_nid_postfix() = errno;
return -1;
int groupReady = 0;
for (auto& group : groups) {
for (auto& entry : group.entries) entry.revents = 0;
const int result = group.poll(group.entries.data(), static_cast<int>(group.entries.size()), slice);
if (result < 0) {
*__error_nid_postfix() = -result;
return -1;
}
for (std::size_t index = 0; index < group.entries.size(); ++index) requested[group.slots[index]].revents = group.entries[index].revents;
groupReady += result;
}
for (std::size_t index = 0; index < hosts.size(); ++index) requested[hostSlots[index]].revents = hosts[index].revents;
for (auto& socket : sockets) socket.revents = 0;
const int socketReady = poller(sockets.data(), static_cast<int>(sockets.size()), SelectPollSliceMilliseconds);
if (socketReady < 0) {
*__error_nid_postfix() = -socketReady;
return -1;
}
for (std::size_t index = 0; index < sockets.size(); ++index) requested[socketSlots[index]].revents = sockets[index].revents;
if (hostReady > 0 || socketReady > 0) break;
if (std::chrono::steady_clock::now() >= sliceEnd) break;
if (groups.size() == 1 || groupReady > 0 || std::chrono::steady_clock::now() >= sliceEnd) break;
}
} else if (!hosts.empty()) {
for (auto& host : hosts) host.revents = 0;
if (HostPoll(hosts.data(), hosts.size(), waitMilliseconds) < 0) {
*__error_nid_postfix() = errno;
return -1;
}
for (std::size_t index = 0; index < hosts.size(); ++index) requested[hostSlots[index]].revents = hosts[index].revents;
} else
#endif
if (!sockets.empty()) {
if (poller == nullptr) UnknownDescriptor(sockets.front().descriptor, false);
for (auto& socket : sockets) socket.revents = 0;
const int result = poller(sockets.data(), static_cast<int>(sockets.size()), waitMilliseconds);
if (result < 0) {
*__error_nid_postfix() = -result;
return -1;
}
for (std::size_t index = 0; index < sockets.size(); ++index) requested[socketSlots[index]].revents = sockets[index].revents;
} else if (waitMilliseconds > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(waitMilliseconds));
}
int ready = 0;
for (const auto& entry : requested) {
+160 -43
View File
@@ -2,6 +2,7 @@
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <fcntl.h>
#include <io.h>
#else
#include <sys/socket.h>
@@ -129,7 +130,56 @@ Waker& ThreadWaker() {
}
std::mutex socketsMutex;
std::map<int, std::shared_ptr<Socket>> sockets;
int nextDescriptor = GuestSockets::FirstDescriptor;
#ifdef _WIN32
int ReserveNumber(NativeSocket) {
const HANDLE placeholder = ::CreateFileW(L"NUL", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (placeholder == INVALID_HANDLE_VALUE)
throw std::runtime_error("socket: opening the NUL descriptor placeholder failed, error " + std::to_string(::GetLastError()));
const int number = ::_open_osfhandle(reinterpret_cast<std::intptr_t>(placeholder), _O_RDONLY);
if (number < 0) {
::CloseHandle(placeholder);
return Fail(24);
}
return number;
}
int CopyNumber(int number) {
const int copy = ::_dup(number);
return copy < 0 ? Fail(errno == EMFILE ? 24 : 9) : copy;
}
int CopyNumberTo(int number, int target) {
return ::_dup2(number, target) != 0 ? Fail(errno == EMFILE ? 24 : 9) : 0;
}
void ReleaseNumber(int number) {
::_close(number);
}
#else
int ReserveNumber(NativeSocket native) {
const int number = ::fcntl(native, F_DUPFD_CLOEXEC, 0);
return number < 0 ? Fail(NativeError()) : number;
}
int CopyNumber(int number) {
const int copy = ::fcntl(number, F_DUPFD_CLOEXEC, 0);
return copy < 0 ? Fail(NativeError()) : copy;
}
int CopyNumberTo(int number, int target) {
return ::dup2(number, target) < 0 ? Fail(NativeError()) : 0;
}
void ReleaseNumber(int number) {
::close(number);
}
#endif
int Install(std::shared_ptr<Socket> socket) {
const int number = ReserveNumber(socket->value);
if (number < 0) return -1;
try {
std::lock_guard lock(socketsMutex);
sockets.insert_or_assign(number, std::move(socket));
} catch (const std::bad_alloc&) {
ReleaseNumber(number);
return Fail(12);
}
return number;
}
std::shared_ptr<Socket> Lookup(int descriptor) {
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
@@ -221,14 +271,31 @@ void GuestAddress(const sockaddr_storage& native, void* output, std::uint32_t* l
}
int GuestSockets::Close(int descriptor) {
std::shared_ptr<Socket> released;
{
std::lock_guard lock(socketsMutex);
if (!sockets.erase(descriptor)) return Fail(9);
const auto found = sockets.find(descriptor);
if (found == sockets.end()) return Fail(9);
released = std::move(found->second);
sockets.erase(found);
}
EqueueDescriptorClosed(descriptor);
ReleaseNumber(descriptor);
return 0;
}
void GuestSockets::Forget(int descriptor) {
std::shared_ptr<Socket> released;
{
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
if (found == sockets.end()) return;
released = std::move(found->second);
sockets.erase(found);
}
EqueueDescriptorClosed(descriptor);
}
std::uintptr_t GuestSockets::CurrentWaker() {
return static_cast<std::uintptr_t>(ThreadWaker().value);
}
@@ -299,6 +366,15 @@ extern "C" bool GuestSocketIsOpen_nid_no_patch(int descriptor) {
return GuestSockets::IsOpen(descriptor);
}
extern "C" int KernelReserveSocketNumber_nid_no_patch(std::uintptr_t native) {
const int number = ReserveNumber(static_cast<NativeSocket>(native));
return number < 0 ? -*__error_nid_postfix() : number;
}
extern "C" void KernelReleaseSocketNumber_nid_no_patch(int number) {
ReleaseNumber(number);
}
namespace {
#ifdef _WIN32
using NativePollDescriptor = WSAPOLLFD;
@@ -389,33 +465,43 @@ bool IsPipe(int descriptor, short events) {
}
int GuestSockets::Duplicate(int descriptor) {
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
if (found == sockets.end()) return Fail(9);
const int duplicate = CopyNumber(descriptor);
if (duplicate < 0) return -1;
try {
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
if (found == sockets.end()) return Fail(9);
if (nextDescriptor == INT_MAX) return Fail(24);
const int duplicate = nextDescriptor;
sockets.emplace(duplicate, found->second);
++nextDescriptor;
return duplicate;
sockets.insert_or_assign(duplicate, found->second);
} catch (const std::bad_alloc&) {
ReleaseNumber(duplicate);
return Fail(12);
}
return duplicate;
}
int GuestSockets::DuplicateTo(int descriptor, int target) {
try {
std::shared_ptr<Socket> replaced;
{
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
if (found == sockets.end()) return Fail(9);
if (target == descriptor) return target;
if (target == INT_MAX) return Fail(9);
sockets.insert_or_assign(target, found->second);
if (target >= nextDescriptor) nextDescriptor = target + 1;
return target;
} catch (const std::bad_alloc&) {
return Fail(12);
if (CopyNumberTo(descriptor, target) != 0) return -1;
const auto previous = sockets.find(target);
if (previous != sockets.end()) {
replaced = std::move(previous->second);
previous->second = found->second;
} else {
try {
sockets.emplace(target, found->second);
} catch (const std::bad_alloc&) {
ReleaseNumber(target);
return Fail(12);
}
}
}
if (replaced) EqueueDescriptorClosed(target);
return target;
}
namespace {
@@ -537,6 +623,51 @@ bool GuestSockets::Ready(int descriptor, bool write, std::int64_t* data, bool* e
return true;
}
int GuestSockets::Poll(KernelSocketPoll::Entry* entries, int count, int timeoutMilliseconds) {
std::vector<std::shared_ptr<Socket>> held;
std::vector<NativePollDescriptor> native;
#ifdef _WIN32
constexpr short NativeReadable = POLLRDNORM;
constexpr short NativeUrgent = POLLRDBAND;
constexpr short NativeWritable = POLLWRNORM;
#else
constexpr short NativeReadable = POLLIN;
constexpr short NativeUrgent = POLLPRI;
constexpr short NativeWritable = POLLOUT;
#endif
for (int index = 0; index < count; ++index) {
auto socket = Find(entries[index].descriptor);
if (!socket) return -9;
NativePollDescriptor request{};
request.fd = socket->value;
if (entries[index].events & KernelSocketPoll::Readable) request.events = static_cast<short>(request.events | NativeReadable);
if (entries[index].events & KernelSocketPoll::Urgent) request.events = static_cast<short>(request.events | NativeUrgent);
if (entries[index].events & KernelSocketPoll::Writable) request.events = static_cast<short>(request.events | NativeWritable);
native.push_back(request);
held.push_back(std::move(socket));
}
if (native.empty()) return 0;
#ifdef _WIN32
const int result = WSAPoll(native.data(), static_cast<ULONG>(native.size()), timeoutMilliseconds);
#else
const int result = ::poll(native.data(), static_cast<nfds_t>(native.size()), timeoutMilliseconds);
#endif
if (result < 0) return -NativeError();
int ready = 0;
for (int index = 0; index < count; ++index) {
const short events = native[static_cast<std::size_t>(index)].revents;
short revents = 0;
if (events & NativeReadable) revents = static_cast<short>(revents | KernelSocketPoll::Readable);
if (events & NativeUrgent) revents = static_cast<short>(revents | KernelSocketPoll::Urgent);
if (events & NativeWritable) revents = static_cast<short>(revents | KernelSocketPoll::Writable);
if (events & (POLLERR | POLLNVAL)) revents = static_cast<short>(revents | KernelSocketPoll::Error);
if (events & POLLHUP) revents = static_cast<short>(revents | KernelSocketPoll::HangUp);
entries[index].revents = revents;
if (revents != 0) ++ready;
}
return ready;
}
extern "C" {
int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...) {
if (descriptor < GuestSockets::FirstDescriptor) {
@@ -636,11 +767,7 @@ int APS5_VABI socket_nid_postfix(int family, int type, int protocol) {
auto socket = std::make_shared<Socket>(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++;
sockets.emplace(descriptor, std::move(socket));
return descriptor;
return Install(std::move(socket));
} catch (const std::bad_alloc&) {
return Fail(12);
}
@@ -669,16 +796,13 @@ int APS5_VABI socketpair_nid_postfix(int family, int type, int protocol, int* de
auto second = std::make_shared<Socket>(native[1], GuestUnix, plainType);
secondGuard.value = Invalid;
if (nonblocking && (SetNonBlocking(*first) || SetNonBlocking(*second))) return -1;
std::lock_guard lock(socketsMutex);
if (nextDescriptor >= INT_MAX - 1) return Fail(24);
const int firstDescriptor = nextDescriptor++;
const int secondDescriptor = nextDescriptor++;
sockets.emplace(firstDescriptor, std::move(first));
try {
sockets.emplace(secondDescriptor, std::move(second));
} catch (const std::bad_alloc&) {
sockets.erase(firstDescriptor);
return Fail(12);
const int firstDescriptor = Install(std::move(first));
if (firstDescriptor < 0) return -1;
const int secondDescriptor = Install(std::move(second));
if (secondDescriptor < 0) {
const int error = *__error_nid_postfix();
GuestSockets::Close(firstDescriptor);
return Fail(error);
}
descriptors[0] = firstDescriptor;
descriptors[1] = secondDescriptor;
@@ -724,10 +848,8 @@ int APS5_VABI accept_nid_postfix(int descriptor, void* address, std::uint32_t* l
inheritNonblocking = listener->nonblocking;
}
if (inheritNonblocking && SetNonBlocking(*accepted)) return -1;
std::lock_guard lock(socketsMutex);
if (nextDescriptor == INT_MAX) return Fail(24);
const int acceptedDescriptor = nextDescriptor++;
sockets.emplace(acceptedDescriptor, std::move(accepted));
const int acceptedDescriptor = Install(std::move(accepted));
if (acceptedDescriptor < 0) return -1;
if (address) GuestAddress(peer, address, length);
return acceptedDescriptor;
} catch (const std::bad_alloc&) {
@@ -1052,7 +1174,8 @@ int APS5_VABI poll_nid_postfix(GuestPollDescriptor* descriptors, std::uint32_t c
if (entry.descriptor < 0) continue;
if ((entry.events & ~GuestPollAccepted) != 0)
throw std::runtime_error("poll: unsupported event flags " + std::to_string(entry.events));
if (entry.descriptor < GuestSockets::FirstDescriptor) {
auto socket = Find(entry.descriptor);
if (!socket) {
if (!DescriptorIsOpen_nid_no_patch(entry.descriptor)) {
entry.revents = GuestPollInvalid;
++ready;
@@ -1068,12 +1191,6 @@ int APS5_VABI poll_nid_postfix(GuestPollDescriptor* descriptors, std::uint32_t c
pipes.push_back(i);
continue;
}
auto socket = Find(entry.descriptor);
if (!socket) {
entry.revents = GuestPollInvalid;
++ready;
continue;
}
#ifdef _WIN32
if (entry.events & GuestPollPriority) throw std::runtime_error("poll: POLLPRI is not supported by WSAPoll");
#endif
+17 -12
View File
@@ -24,7 +24,8 @@ static void Require(bool value) { if (!value) std::abort(); }
constexpr int GuestEbadf = 9;
constexpr int Unix = 1;
constexpr int Stream = 1;
constexpr int FirstSocket = 0x10000000;
constexpr int DescriptorSetSize = 1024;
constexpr int Unopened = 0x1ffffff0;
static void RequireFailure(int result, int error) {
Require(result == -1);
@@ -60,7 +61,7 @@ static void DupOutlivesOriginal() {
int pipe[2];
Require(pipe_nid_postfix(pipe) == 0);
const int duplicate = dup_nid_postfix(pipe[1]);
Require(duplicate >= 0 && duplicate < FirstSocket && duplicate != pipe[1]);
Require(duplicate >= 0 && duplicate != pipe[1]);
Require(close_nid_postfix(pipe[1]) == 0);
RequirePipeCarries(duplicate, pipe[0], "dup");
Require(close_nid_postfix(duplicate) == 0);
@@ -97,8 +98,8 @@ static void BadDescriptors() {
RequireFailure(dup2_nid_postfix(closed, closed), GuestEbadf);
RequireFailure(dup2_nid_postfix(pipe[0], -1), GuestEbadf);
RequireFailure(dup2_nid_postfix(-1, pipe[0]), GuestEbadf);
RequireFailure(dup_nid_postfix(FirstSocket + 0x0ffffff0), GuestEbadf);
RequireFailure(dup2_nid_postfix(FirstSocket + 0x0ffffff0, FirstSocket + 0x0ffffff1), GuestEbadf);
RequireFailure(dup_nid_postfix(Unopened), GuestEbadf);
RequireFailure(dup2_nid_postfix(Unopened, Unopened + 1), GuestEbadf);
#ifdef _WIN32
const auto previous = _set_invalid_parameter_handler(RecordInvalidParameter);
#endif
@@ -115,25 +116,29 @@ static void SocketDuplicates() {
int pair[2];
Require(socketpair_nid_postfix(Unix, Stream, 0, pair) == 0);
const int duplicate = dup_nid_postfix(pair[0]);
Require(duplicate >= FirstSocket && duplicate != pair[0] && duplicate != pair[1]);
Require(duplicate >= 0 && duplicate < DescriptorSetSize && duplicate != pair[0] && duplicate != pair[1]);
Require(close_nid_postfix(pair[0]) == 0);
RequireSocketCarries(duplicate, pair[1], "socket");
const int target = duplicate + 0x1000;
int pipe[2];
Require(pipe_nid_postfix(pipe) == 0);
const int target = pipe[0];
Require(dup2_nid_postfix(duplicate, target) == target);
Require(close_nid_postfix(duplicate) == 0);
RequireSocketCarries(target, pair[1], "moved");
Require(dup2_nid_postfix(target, target) == target);
int next[2];
Require(socketpair_nid_postfix(Unix, Stream, 0, next) == 0);
Require(next[0] > target && next[1] > target);
Require(next[0] < DescriptorSetSize && next[1] < DescriptorSetSize);
RequireSocketCarries(target, pair[1], "kept");
Require(dup2_nid_postfix(next[0], target) == target);
RequireSocketCarries(target, next[1], "over");
Require(Throws([&] { dup2_nid_postfix(target, 1); }));
int pipe[2];
Require(pipe_nid_postfix(pipe) == 0);
Require(Throws([&] { dup2_nid_postfix(pipe[0], target); }));
for (const int descriptor : {target, pair[1], next[0], next[1], pipe[0], pipe[1]}) Require(close_nid_postfix(descriptor) == 0);
int other[2];
Require(pipe_nid_postfix(other) == 0);
Require(dup2_nid_postfix(other[1], target) == target);
RequirePipeCarries(target, other[0], "file");
Require(send_nid_postfix(target, "x", 1, 0) == -1);
for (const int descriptor : {target, pair[1], next[0], next[1], pipe[1], other[0], other[1]}) Require(close_nid_postfix(descriptor) == 0);
}
static void RandomDeviceDuplicates() {
@@ -171,7 +176,7 @@ static void RandomDeviceDuplicates() {
Require(read_nid_postfix(secondRandom, randomBuf4, sizeof(randomBuf4)) == sizeof(randomBuf4));
Require(std::memcmp(randomBuf4, randomBuf3, sizeof(randomBuf4)) != 0);
const int invalidFd = FirstSocket - 100;
const int invalidFd = Unopened - 100;
RequireFailure(dup2_nid_postfix(invalidFd, secondRandom), GuestEbadf);
unsigned char randomBuf5[16] = {};
Require(read_nid_postfix(secondRandom, randomBuf5, sizeof(randomBuf5)) == sizeof(randomBuf5));
+2 -2
View File
@@ -57,7 +57,7 @@ static constexpr std::uint32_t StatfsVersion = 0x20030518;
static constexpr int GuestEbadf = 9;
static constexpr int GuestEfault = 14;
static constexpr int GuestEinval = 22;
static constexpr int FirstSocket = 0x10000000;
static constexpr int DescriptorSetSize = 1024;
static void RequireFailure(int descriptor, int error) {
GuestStatfs fs;
@@ -116,7 +116,7 @@ int main() {
int sockets[2];
Require(socketpair_nid_postfix(1, 1, 0, sockets) == 0);
Require(sockets[0] >= FirstSocket);
Require(sockets[0] >= 0 && sockets[0] < DescriptorSetSize);
RequireFailure(sockets[0], GuestEinval);
Require(close_nid_postfix(sockets[0]) == 0);
Require(close_nid_postfix(sockets[1]) == 0);
+13 -1
View File
@@ -21,6 +21,7 @@ int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*);
std::int64_t APS5_VABI send_nid_postfix(int, const void*, std::uint64_t, int);
std::int64_t APS5_VABI recv_nid_postfix(int, void*, std::uint64_t, int);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI open_nid_postfix(const char*, int, int);
int APS5_VABI poll_nid_postfix(PollDescriptor*, std::uint32_t, int);
int APS5_VABI pipe_nid_postfix(int*);
std::int64_t APS5_VABI read_nid_postfix(int, void*, std::uint64_t);
@@ -110,7 +111,7 @@ static void TestPipes(int socket) {
}
int main() {
const int listener = socket_nid_postfix(2, 1, 0);
Require(listener >= 0);
Require(listener >= 0 && listener < 1024);
std::array<unsigned char, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
@@ -145,5 +146,16 @@ int main() {
Require(poll_nid_postfix(&single, 1, -2) == -1 && *__error_nid_postfix() == 22);
TestPipes(listener);
Require(close_nid_postfix(server) == 0);
const int file = open_nid_postfix("/dev/urandom", 0, 0);
Require(file >= 0);
PollDescriptor mixed[2]{{listener, In, 0}, {file, In, 0}};
bool unsupported = false;
try {
poll_nid_postfix(mixed, 2, 0);
} catch (const std::runtime_error&) {
unsupported = true;
}
Require(unsupported);
Require(close_nid_postfix(file) == 0);
Require(close_nid_postfix(listener) == 0);
}
+1 -2
View File
@@ -1,5 +1,4 @@
#include "prx/libc/include/General.hpp"
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
#include <array>
#include <cstdio>
#include <cstdlib>
@@ -111,7 +110,7 @@ int main() {
Require(close_nid_postfix(pipe) == 0);
}
const int socket = socket_nid_postfix(2, 2, 0);
Require(socket >= GuestSockets::FirstDescriptor);
Require(socket >= 0 && socket < 1024);
Require(isatty_nid_postfix(socket) == 0 && *__error_nid_postfix() == 25);
Require(close_nid_postfix(socket) == 0);
Require(isatty_nid_postfix(socket) == 0 && *__error_nid_postfix() == 9);
+14 -1
View File
@@ -59,6 +59,8 @@ int APS5_VABI sceNetResolverGetError(int, int*);
int APS5_VABI sceNetCtlGetState(int*);
int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*);
int APS5_VABI open_nid_postfix(const char*, int, int);
int APS5_VABI socket_nid_postfix(int, int, int);
int APS5_VABI bind_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI setsockopt_nid_postfix(int, int, int, const void*, std::uint32_t);
int* APS5_VABI __error_nid_postfix();
@@ -578,7 +580,7 @@ int main() {
const int udp_receiver = sceNetSocket(nullptr, 2, 2, 17);
const int udp_sender = sceNetSocket(nullptr, 2, 2, 17);
Require(udp_receiver >= 0 && udp_sender >= 0);
Require(udp_receiver >= 0 && udp_sender >= 0 && udp_receiver < 1024 && udp_sender < 1024);
address = {16, 2, 0, 0, 127, 0, 0, 1};
Require(sceNetBind_nid_postfix(udp_receiver, address.data(), address.size()) == 0);
address_size = address.size();
@@ -621,6 +623,17 @@ int main() {
Require(((random_readable[udp_receiver / 64] >> (udp_receiver % 64)) & 1u) == 0);
Require(random_urgent[random / 64] == 0);
Require(close_nid_postfix(random) == 0);
const int kernel_socket = socket_nid_postfix(2, 2, 0);
Require(kernel_socket >= 0 && kernel_socket < 1024 && kernel_socket != udp_receiver && kernel_socket != udp_sender);
std::array<std::uint8_t, 16> kernel_address{16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(kernel_socket, kernel_address.data(), kernel_address.size()) == 0);
std::uint64_t mixed_writable[16]{};
mixed_writable[kernel_socket / 64] |= std::uint64_t{1} << (kernel_socket % 64);
mixed_writable[udp_sender / 64] |= std::uint64_t{1} << (udp_sender % 64);
Require(select_nid_postfix(std::max(kernel_socket, udp_sender) + 1, nullptr, mixed_writable, nullptr, wait_second) == 2);
Require(((mixed_writable[kernel_socket / 64] >> (kernel_socket % 64)) & 1u) != 0);
Require(((mixed_writable[udp_sender / 64] >> (udp_sender % 64)) & 1u) != 0);
Require(close_nid_postfix(kernel_socket) == 0);
std::uint64_t stdio_readable[16]{1};
std::uint64_t stdio_writable[16]{6};
Require(select_nid_postfix(3, stdio_readable, stdio_writable, nullptr, poll_now) == 2);
+44
View File
@@ -25,6 +25,12 @@ extern "C" {
int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*);
int APS5_VABI pipe_nid_postfix(int*);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI socket_nid_postfix(int, int, int);
int APS5_VABI bind_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*);
int APS5_VABI connect_nid_postfix(int, const void*, std::uint32_t);
std::int64_t APS5_VABI send_nid_postfix(int, const void*, std::uint64_t, int);
std::int64_t APS5_VABI recv_nid_postfix(int, void*, std::uint64_t, int);
std::int64_t APS5_VABI write_nid_postfix(int, const char*, std::int64_t);
std::int64_t APS5_VABI read_nid_postfix(int, void*, std::uint64_t);
int* APS5_VABI __error_nid_postfix();
@@ -106,6 +112,44 @@ int main() {
Require(*__error_nid_postfix() == GuestEinval);
}
{
int pipe[2]{-1, -1};
Require(pipe_nid_postfix(pipe) == 0);
const int receiver = socket_nid_postfix(2, 2, 0);
const int sender = socket_nid_postfix(2, 2, 0);
Require(receiver >= 0 && receiver < GuestDescriptorLimit && sender >= 0 && sender < GuestDescriptorLimit);
std::array<unsigned char, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(receiver, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(getsockname_nid_postfix(receiver, address.data(), &size) == 0);
Require(connect_nid_postfix(sender, address.data(), address.size()) == 0);
const int highest = receiver > pipe[0] ? receiver : pipe[0];
GuestDescriptorSet idle{};
idle.Add(receiver);
idle.Add(pipe[0]);
GuestTimeval none{0, 0};
Require(select_nid_postfix(highest + 1, idle.words.data(), nullptr, nullptr, &none) == 0);
Require(!idle.Has(receiver) && !idle.Has(pipe[0]));
const char byte = 'm';
Require(send_nid_postfix(sender, &byte, 1, 0) == 1);
Require(write_nid_postfix(pipe[1], &byte, 1) == 1);
GuestDescriptorSet read{};
GuestTimeval limit{2, 0};
const auto start = std::chrono::steady_clock::now();
int ready = 0;
while (ready < 2 && std::chrono::steady_clock::now() - start < std::chrono::seconds(2)) {
read = {};
read.Add(receiver);
read.Add(pipe[0]);
ready = select_nid_postfix(highest + 1, read.words.data(), nullptr, nullptr, &limit);
}
Require(ready == 2 && read.Has(receiver) && read.Has(pipe[0]));
char buffer[1]{};
Require(recv_nid_postfix(receiver, buffer, sizeof(buffer), 0) == 1 && buffer[0] == byte);
Require(read_nid_postfix(pipe[0], buffer, sizeof(buffer)) == 1 && buffer[0] == byte);
for (const int descriptor : {receiver, sender, pipe[0], pipe[1]}) Require(close_nid_postfix(descriptor) == 0);
}
Require(close_nid_postfix(reader) == 0);
Require(close_nid_postfix(writer) == 0);
+185
View File
@@ -0,0 +1,185 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <stdexcept>
#include <vector>
struct GuestTimeval {
std::int64_t seconds;
std::int64_t microseconds;
};
struct GuestDescriptorSet {
std::array<std::uint64_t, 1024 / 64> words{};
void Add(int descriptor) {
words[descriptor / 64] |= (std::uint64_t{1} << (descriptor % 64));
}
bool Has(int descriptor) const {
return ((words[descriptor / 64] >> (descriptor % 64)) & 1u) != 0;
}
};
struct PollDescriptor {
int descriptor;
short events;
short revents;
};
extern "C" {
int APS5_VABI socket_nid_postfix(int, int, int);
int APS5_VABI bind_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*);
int APS5_VABI connect_nid_postfix(int, const void*, std::uint32_t);
std::int64_t APS5_VABI send_nid_postfix(int, const void*, std::uint64_t, int);
std::int64_t APS5_VABI recv_nid_postfix(int, void*, std::uint64_t, int);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI sceKernelClose(int);
int APS5_VABI dup_nid_postfix(int);
int APS5_VABI open_nid_postfix(const char*, int, int);
std::int64_t APS5_VABI read_nid_postfix(int, void*, std::uint64_t);
int APS5_VABI select_nid_postfix(int, void*, void*, void*, const void*);
int APS5_VABI poll_nid_postfix(PollDescriptor*, std::uint32_t, int);
int* APS5_VABI __error_nid_postfix();
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "socket descriptor check failed at line %d, errno %d\n", line, *__error_nid_postfix());
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
constexpr int DescriptorSetSize = 1024;
constexpr int Inet = 2;
constexpr int Datagram = 2;
constexpr short In = 0x1;
constexpr int KernelBadDescriptor = static_cast<int>(0x80020009u);
template <typename TCall> static bool Throws(TCall call) {
try {
call();
} catch (const std::runtime_error&) {
return true;
}
return false;
}
static int Socket() {
const int descriptor = socket_nid_postfix(Inet, Datagram, 0);
Require(descriptor >= 0 && descriptor < DescriptorSetSize);
return descriptor;
}
static int BoundSocket(std::array<unsigned char, 16>& address) {
const int descriptor = Socket();
address = {16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(descriptor, address.data(), address.size()) == 0);
std::uint32_t size = address.size();
Require(getsockname_nid_postfix(descriptor, address.data(), &size) == 0);
return descriptor;
}
static int ConnectedSocket(const std::array<unsigned char, 16>& address) {
const int descriptor = Socket();
Require(connect_nid_postfix(descriptor, address.data(), address.size()) == 0);
return descriptor;
}
static void WaitReadable(int descriptor) {
GuestDescriptorSet read{};
read.Add(descriptor);
GuestTimeval limit{2, 0};
Require(select_nid_postfix(descriptor + 1, read.words.data(), nullptr, nullptr, &limit) == 1);
Require(read.Has(descriptor));
}
static void SelectSocketAndFile() {
std::array<unsigned char, 16> address{};
const int receiver = BoundSocket(address);
const int sender = ConnectedSocket(address);
const int file = open_nid_postfix("/dev/urandom", 0, 0);
Require(file >= 0 && file < DescriptorSetSize && file != receiver && file != sender);
const int highest = std::max({receiver, sender, file});
{
GuestDescriptorSet read{};
read.Add(receiver);
GuestTimeval none{0, 0};
Require(select_nid_postfix(receiver + 1, read.words.data(), nullptr, nullptr, &none) == 0);
Require(!read.Has(receiver));
}
{
GuestDescriptorSet read{};
read.Add(receiver);
read.Add(file);
GuestTimeval none{0, 0};
Require(select_nid_postfix(highest + 1, read.words.data(), nullptr, nullptr, &none) == 1);
Require(read.Has(file) && !read.Has(receiver));
}
const char byte = 'd';
Require(send_nid_postfix(sender, &byte, 1, 0) == 1);
WaitReadable(receiver);
{
GuestDescriptorSet read{};
GuestDescriptorSet write{};
read.Add(receiver);
read.Add(file);
write.Add(sender);
GuestTimeval none{0, 0};
Require(select_nid_postfix(highest + 1, read.words.data(), write.words.data(), nullptr, &none) == 3);
Require(read.Has(receiver) && read.Has(file) && write.Has(sender));
}
char received = 0;
Require(recv_nid_postfix(receiver, &received, 1, 0) == 1 && received == byte);
for (const int descriptor : {receiver, sender, file}) Require(close_nid_postfix(descriptor) == 0);
}
static void CloseReleasesNumber() {
const int socket = Socket();
Require(close_nid_postfix(socket) == 0);
std::vector<int> files;
while (files.size() < 8 && (files.empty() || files.back() != socket)) {
const int file = open_nid_postfix("/dev/urandom", 0, 0);
Require(file >= 0);
files.push_back(file);
}
Require(files.back() == socket);
unsigned char bytes[4]{};
Require(read_nid_postfix(socket, bytes, sizeof(bytes)) == sizeof(bytes));
PollDescriptor entry{socket, In, 0};
Require(Throws([&] { poll_nid_postfix(&entry, 1, 0); }));
for (const int file : files) Require(close_nid_postfix(file) == 0);
const int kernelClosed = Socket();
Require(sceKernelClose(kernelClosed) == 0);
Require(sceKernelClose(kernelClosed) == KernelBadDescriptor);
entry = {kernelClosed, In, 0};
Require(poll_nid_postfix(&entry, 1, 0) == 1 && entry.revents == 0x20);
}
static void DuplicateKeepsSocket() {
std::array<unsigned char, 16> address{};
const int receiver = BoundSocket(address);
const int sender = ConnectedSocket(address);
const int duplicate = dup_nid_postfix(receiver);
Require(duplicate >= 0 && duplicate < DescriptorSetSize && duplicate != receiver && duplicate != sender);
Require(close_nid_postfix(receiver) == 0);
const char byte = 'u';
Require(send_nid_postfix(sender, &byte, 1, 0) == 1);
WaitReadable(duplicate);
char received = 0;
Require(recv_nid_postfix(duplicate, &received, 1, 0) == 1 && received == byte);
Require(close_nid_postfix(duplicate) == 0);
Require(close_nid_postfix(sender) == 0);
}
int main() {
SelectSocketAndFile();
CloseReleasesNumber();
DuplicateKeepsSocket();
return 0;
}
+2 -2
View File
@@ -51,7 +51,7 @@ constexpr int CloseOnExec = 0x10000000;
constexpr int NonBlocking = 0x20000000;
constexpr int NoSignal = 0x20000;
constexpr int Peek = 2;
constexpr int FirstSocket = 0x10000000;
constexpr int DescriptorSetSize = 1024;
constexpr short In = 0x1;
constexpr short Out = 0x4;
constexpr short Hup = 0x10;
@@ -90,7 +90,7 @@ static bool Unnamed(int descriptor, int (APS5_VABI *query)(int, void*, std::uint
int main() {
int pair[2] = {-1, -1};
Require(socketpair_nid_postfix(Unix, Stream, 0, pair) == 0);
Require(pair[0] >= FirstSocket && pair[1] >= FirstSocket && pair[0] != pair[1]);
Require(pair[0] >= 0 && pair[0] < DescriptorSetSize && pair[1] >= 0 && pair[1] < DescriptorSetSize && pair[0] != pair[1]);
Require(Exchange(pair[0], pair[1], "one way"));
Require(Exchange(pair[1], pair[0], "other way"));
Require(Unnamed(pair[0], getsockname_nid_postfix) && Unnamed(pair[1], getpeername_nid_postfix));