Files
AnyPS5/core/libs/tests/GuestSocketReadWrite.cpp
T
Jan ŠléglandClaude Fable 5.1 9d0b105771 fix(libkernel): read and write guest sockets
read, write, _read and _write on a guest socket descriptor went to the
host, which has no such descriptor, so sceKernelRead and sceKernelWrite
threw. They now reach the socket as recv and send without flags, with
the socket layer's errno (EAGAIN 35, EPIPE 32, EBADF 9), a zero-length
read returns 0 as dofileread does, and the two sceKernel forms return
the kernel error for the same cases. The host's SIGPIPE on EPIPE, a
write above INT_MAX bytes, readv, writev and the positioned forms are
recorded in TechnicalDebt.

OpenSSL's QUIC notifier signals its socketpair with write and read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 23:57:33 +02:00

115 lines
5.1 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifndef _WIN32
#include <csignal>
#endif
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 listen_nid_postfix(int, int);
int APS5_VABI getsockname_nid_postfix(int, void*, std::uint32_t*);
int APS5_VABI connect_nid_postfix(int, const void*, std::uint32_t);
int APS5_VABI accept_nid_postfix(int, void*, std::uint32_t*);
int APS5_VABI fcntl_nid_postfix(int, int, ...);
int APS5_VABI poll_nid_postfix(PollDescriptor*, std::uint32_t, int);
int APS5_VABI close_nid_postfix(int);
std::int64_t APS5_VABI read_nid_postfix(int, void*, std::uint64_t);
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::size_t);
std::int64_t APS5_VABI _write_nid_postfix(int, const void*, std::size_t);
std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t);
std::int64_t APS5_VABI sceKernelWrite(int, const void*, std::size_t);
int* APS5_VABI __error_nid_postfix();
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "socket read/write check failed at line %d, errno %d\n", line, *__error_nid_postfix());
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
constexpr short In = 0x1;
constexpr short Hup = 0x10;
constexpr int KernelAgain = static_cast<int>(0x80020023u);
constexpr int KernelBadDescriptor = static_cast<int>(0x80020009u);
static bool Readable(int descriptor, short accepted = In) {
PollDescriptor waited{descriptor, In, 0};
return poll_nid_postfix(&waited, 1, 2000) == 1 && (waited.revents & accepted) != 0;
}
int main() {
#ifndef _WIN32
std::signal(SIGPIPE, SIG_IGN);
#endif
const int listener = socket_nid_postfix(2, 1, 0);
Require(listener >= 0);
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
Require(bind_nid_postfix(listener, address.data(), address.size()) == 0);
Require(listen_nid_postfix(listener, 4) == 0);
std::uint32_t addressSize = address.size();
Require(getsockname_nid_postfix(listener, address.data(), &addressSize) == 0);
const int client = socket_nid_postfix(2, 1, 0);
Require(client >= 0 && connect_nid_postfix(client, address.data(), address.size()) == 0);
const int server = accept_nid_postfix(listener, nullptr, nullptr);
Require(server >= 0);
const char request[] = "written to a socket";
char received[32]{};
Require(write_nid_postfix(client, request, sizeof(request)) == sizeof(request));
Require(Readable(server) && read_nid_postfix(server, received, sizeof(received)) == sizeof(request));
Require(std::strcmp(received, request) == 0);
const char reply[] = "and back";
std::memset(received, 0, sizeof(received));
Require(_write_nid_postfix(server, reply, sizeof(reply)) == sizeof(reply));
Require(Readable(client) && _read_nid_postfix(client, received, sizeof(received)) == sizeof(reply));
Require(std::strcmp(received, reply) == 0);
const char kernel[] = "through sceKernelWrite";
std::memset(received, 0, sizeof(received));
Require(sceKernelWrite(client, kernel, sizeof(kernel)) == sizeof(kernel));
Require(Readable(server) && sceKernelRead(server, received, sizeof(received)) == sizeof(kernel));
Require(std::strcmp(received, kernel) == 0);
Require(fcntl_nid_postfix(server, 4, 4) == 0);
*__error_nid_postfix() = 0;
Require(read_nid_postfix(server, received, sizeof(received)) == -1 && *__error_nid_postfix() == 35);
Require(sceKernelRead(server, received, sizeof(received)) == KernelAgain);
Require(read_nid_postfix(server, received, 0) == 0 && sceKernelRead(server, received, 0) == 0);
Require(close_nid_postfix(client) == 0);
Require(Readable(server, In | Hup) && read_nid_postfix(server, received, sizeof(received)) == 0);
Require(sceKernelRead(server, received, sizeof(received)) == 0);
#ifndef _WIN32
std::int64_t written = 0;
for (int i = 0; i < 100 && written >= 0; ++i) written = write_nid_postfix(server, request, sizeof(request));
Require(written == -1 && *__error_nid_postfix() == 32);
#endif
Require(close_nid_postfix(server) == 0);
*__error_nid_postfix() = 0;
Require(read_nid_postfix(server, received, sizeof(received)) == -1 && *__error_nid_postfix() == 9);
Require(read_nid_postfix(server, received, 0) == -1 && *__error_nid_postfix() == 9);
Require(write_nid_postfix(server, request, sizeof(request)) == -1 && *__error_nid_postfix() == 9);
Require(_write_nid_postfix(server, request, sizeof(request)) == -1 && *__error_nid_postfix() == 9);
Require(_read_nid_postfix(server, received, sizeof(received)) == -1 && *__error_nid_postfix() == 9);
Require(sceKernelRead(server, received, sizeof(received)) == KernelBadDescriptor);
Require(sceKernelWrite(server, request, sizeof(request)) == KernelBadDescriptor);
Require(close_nid_postfix(listener) == 0);
return 0;
}