Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/libs/prx/libkernel/File/src/Open.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ static int MapFlags(int sceFlags) {

extern "C" int* APS5_VABI __error_nid_postfix();
extern "C" int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...);
static constexpr int GuestSetFlags = 4;

static int SceErrorFromErrno(int error) {
const int guest = error > 0 && error <= 34 ? error : FilesystemError(std::error_code(error, std::generic_category()));
Expand Down Expand Up @@ -414,7 +413,8 @@ int APS5_VABI sceKernelFcntl(int d, int command, ...) {
}
int result;
if (GuestSockets::IsOpen(d)) {
if (command == GuestSetFlags) result = fcntl_nid_postfix(d, command, static_cast<int>(argument));
if (type == File::FcntlArgument::Int) result = fcntl_nid_postfix(d, command, static_cast<int>(argument));
else if (type == File::FcntlArgument::Pointer) result = fcntl_nid_postfix(d, command, reinterpret_cast<void*>(argument));
else result = fcntl_nid_postfix(d, command);
} else {
result = File::Fcntl(d, command, argument);
Expand Down
97 changes: 52 additions & 45 deletions core/libs/prx/libkernel/Socket/src/Udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <vector>

extern "C" int* APS5_VABI __error_nid_postfix();
extern "C" int APS5_VABI dup2_nid_postfix(int from, int to);
namespace {
#ifdef _WIN32
using NativeSocket = SOCKET;
Expand Down Expand Up @@ -180,9 +181,20 @@ int ReserveNumber(NativeSocket) {
}
return number;
}
int CopyNumber(int number) {
const int copy = ::_dup(number);
return copy < 0 ? Fail(errno == EMFILE ? 24 : 9) : copy;
int CopyNumber(int number, int minimum = 0) {
std::vector<int> below;
int copy = -1;
int error = 0;
try {
while ((copy = ::_dup(number)) >= 0 && copy < minimum) below.push_back(copy);
if (copy < 0) error = errno == EMFILE ? 24 : 9;
} catch (const std::bad_alloc&) {
if (copy >= 0) ::_close(copy);
copy = -1;
error = 12;
}
for (const int temporary : below) ::_close(temporary);
return copy < 0 ? Fail(error) : copy;
}
int CopyNumberTo(int number, int target) {
return ::_dup2(number, target) != 0 ? Fail(errno == EMFILE ? 24 : 9) : 0;
Expand All @@ -195,8 +207,8 @@ 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);
int CopyNumber(int number, int minimum = 0) {
const int copy = ::fcntl(number, F_DUPFD_CLOEXEC, minimum);
return copy < 0 ? Fail(NativeError()) : copy;
}
int CopyNumberTo(int number, int target) {
Expand Down Expand Up @@ -560,21 +572,30 @@ bool GuestSockets::PipeReady(int descriptor, short events, short& revents) {
}
#endif

int GuestSockets::Duplicate(int descriptor) {
namespace {
int DuplicateSocket_nid_no_patch(int descriptor, int minimum, bool closeOnExec) {
if (minimum < 0) return Fail(22);
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
if (found == sockets.end()) return Fail(9);
const int duplicate = CopyNumber(descriptor);
const int duplicate = CopyNumber(descriptor, minimum);
if (duplicate < 0) return -1;
try {
sockets.insert_or_assign(duplicate, found->second);
closeOnExecDescriptors.erase(duplicate);
if (closeOnExec) closeOnExecDescriptors.insert(duplicate);
else closeOnExecDescriptors.erase(duplicate);
} catch (const std::bad_alloc&) {
sockets.erase(duplicate);
ReleaseNumber(duplicate);
return Fail(12);
}
return duplicate;
}
}

int GuestSockets::Duplicate(int descriptor) {
return DuplicateSocket_nid_no_patch(descriptor, 0, false);
}

int GuestSockets::DuplicateTo(int descriptor, int target) {
std::shared_ptr<Socket> replaced;
Expand Down Expand Up @@ -886,31 +907,37 @@ extern "C" bool KernelLookupSocketNative_nid_no_patch(int descriptor, std::share

extern "C" {
int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...) {
if (!GuestSockets::IsOpen(descriptor)) {
const auto type = File::FcntlArgumentOf(command);
std::intptr_t argument = 0;
if (type != File::FcntlArgument::None) {
const auto type = File::FcntlArgumentOf(command);
std::intptr_t argument = 0;
if (type != File::FcntlArgument::None) {
#ifdef _WIN32
__builtin_sysv_va_list arguments;
__builtin_sysv_va_start(arguments, command);
argument = type == File::FcntlArgument::Int ? __builtin_va_arg(arguments, int)
: reinterpret_cast<std::intptr_t>(__builtin_va_arg(arguments, void*));
__builtin_sysv_va_end(arguments);
__builtin_sysv_va_list arguments;
__builtin_sysv_va_start(arguments, command);
argument = type == File::FcntlArgument::Int ? __builtin_va_arg(arguments, int)
: reinterpret_cast<std::intptr_t>(__builtin_va_arg(arguments, void*));
__builtin_sysv_va_end(arguments);
#else
std::va_list arguments;
va_start(arguments, command);
argument = type == File::FcntlArgument::Int ? va_arg(arguments, int)
: reinterpret_cast<std::intptr_t>(va_arg(arguments, void*));
va_end(arguments);
std::va_list arguments;
va_start(arguments, command);
argument = type == File::FcntlArgument::Int ? va_arg(arguments, int)
: reinterpret_cast<std::intptr_t>(va_arg(arguments, void*));
va_end(arguments);
#endif
}
}
if (!GuestSockets::IsOpen(descriptor)) {
int result = 0;
int netError = 0;
if (KernelControlSocketStatus_nid_no_patch(descriptor, command, static_cast<int>(argument), &result, &netError)) {
return netError == 0 ? result : Fail(netError);
}
return File::Fcntl(descriptor, command, argument);
}
if (command == 0 || command == 17) return DuplicateSocket_nid_no_patch(descriptor, static_cast<int>(argument), command == 17);
if (command == 10 || command == 18) {
const int duplicate = dup2_nid_postfix(descriptor, static_cast<int>(argument));
if (duplicate >= 0 && command == 18) fcntl_nid_postfix(duplicate, 2, 1);
return duplicate;
}
const auto socket = Lookup(descriptor);
if (!socket) return -1;
std::lock_guard lock(socket->modeMutex);
Expand All @@ -919,35 +946,15 @@ int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...) {
return closeOnExecDescriptors.count(descriptor) != 0 ? 1 : 0;
}
if (command == 2) {
#ifdef _WIN32
__builtin_sysv_va_list arguments;
__builtin_sysv_va_start(arguments, command);
const int flags = __builtin_va_arg(arguments, int);
__builtin_sysv_va_end(arguments);
#else
std::va_list arguments;
va_start(arguments, command);
const int flags = va_arg(arguments, int);
va_end(arguments);
#endif
const int flags = static_cast<int>(argument);
std::lock_guard registry(socketsMutex);
if ((flags & 1) != 0) closeOnExecDescriptors.insert(descriptor);
else closeOnExecDescriptors.erase(descriptor);
return 0;
}
if (command == 3) return 2 | (socket->options.nonblocking ? 4 : 0); // F_GETFL, O_RDWR
if (command != 4) return Fail(22);
#ifdef _WIN32
__builtin_sysv_va_list arguments;
__builtin_sysv_va_start(arguments, command);
const int flags = __builtin_va_arg(arguments, int);
__builtin_sysv_va_end(arguments);
#else
std::va_list arguments;
va_start(arguments, command);
const int flags = va_arg(arguments, int);
va_end(arguments);
#endif
const int flags = static_cast<int>(argument);
if ((flags & ~7) != 0) return Fail(45);
#ifdef _WIN32
unsigned long enabled = (flags & 4) != 0;
Expand Down
65 changes: 64 additions & 1 deletion core/libs/tests/GuestKernelFcntl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ int* APS5_VABI __error_nid_postfix();
int APS5_VABI sceKernelFcntl(int, int, ...);
int APS5_VABI sceKernelOpen(const char*, int, std::uint16_t);
int APS5_VABI sceKernelClose(int);
std::int64_t APS5_VABI sceKernelRead(int, void*, std::size_t);
std::int64_t APS5_VABI sceKernelLseek(int, std::int64_t, int);
}
static void Require(bool value) { if (!value) std::abort(); }
Expand All @@ -39,12 +40,14 @@ static constexpr int SetFlags = 4;
static constexpr int OldGetLock = 7;
static constexpr int OldSetLock = 8;
static constexpr int OldSetLockWait = 9;
static constexpr int Duplicate2Command = 10;
static constexpr int GetLock = 11;
static constexpr int SetLock = 12;
static constexpr int SetLockWait = 13;
static constexpr int ReadAhead = 15;
static constexpr int DarwinReadAhead = 16;
static constexpr int DuplicateCloseOnExec = 17;
static constexpr int Duplicate2CloseOnExec = 18;
static constexpr int ReadOnly = 0;
static constexpr int WriteOnly = 1;
static constexpr int ReadWrite = 2;
Expand Down Expand Up @@ -107,13 +110,73 @@ static void SocketsKeepTheirPath() {
Require(sceKernelFcntl(socket, GetFlags) == (ReadWrite | NonBlocking));
Require(sceKernelFcntl(socket, SetFlags, 0) == 0);
Require(sceKernelFcntl(socket, GetFlags) == ReadWrite);
GuestLock lock{};
Require(sceKernelFcntl(socket, GetLock, &lock) == SceEinval);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 0);
Require(sceKernelFcntl(socket, SetDescriptorFlags, 1) == 0);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 1);
const int duplicate = sceKernelFcntl(socket, DuplicateCommand, 20);
Require(duplicate >= 20);
Require(sceKernelFcntl(duplicate, GetDescriptorFlags) == 0);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 1);
Require(sceKernelFcntl(socket, SetFlags, ReadWrite | NonBlocking) == 0);
Require(sceKernelFcntl(duplicate, GetFlags) == (ReadWrite | NonBlocking));
Require(sceKernelFcntl(duplicate, SetFlags, 0) == 0);
Require(sceKernelFcntl(socket, GetFlags) == ReadWrite);
const int closing = sceKernelFcntl(socket, DuplicateCloseOnExec, duplicate + 1);
Require(closing >= duplicate + 1);
Require(sceKernelFcntl(closing, GetDescriptorFlags) == 1);
Require(sceKernelFcntl(socket, SetDescriptorFlags, 0) == 0);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 0);
Require(fcntl_nid_postfix(socket, Duplicate2Command, closing) == closing);
Require(sceKernelFcntl(closing, GetDescriptorFlags) == 0);
Require(sceKernelFcntl(socket, Duplicate2CloseOnExec, closing) == closing);
Require(sceKernelFcntl(closing, GetDescriptorFlags) == 1);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 0);
Require(sceKernelFcntl(socket, Duplicate2CloseOnExec, socket) == socket);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 1);
Require(sceKernelFcntl(socket, Duplicate2Command, socket) == socket);
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 1);
Require(sceKernelFcntl(socket, SetDescriptorFlags, 0) == 0);
int pipe[2] = {-1, -1};
Require(pipe_nid_postfix(pipe) == 0);
Require(fcntl_nid_postfix(pipe[0], SetDescriptorFlags, 1) == 0);
Require(fcntl_nid_postfix(socket, Duplicate2Command, pipe[0]) == pipe[0]);
Require(sceKernelFcntl(pipe[0], GetFlags) == ReadWrite);
Require(sceKernelFcntl(pipe[0], GetDescriptorFlags) == 0);
Require(sceKernelFcntl(socket, Duplicate2CloseOnExec, pipe[1]) == pipe[1]);
Require(sceKernelFcntl(pipe[1], GetFlags) == ReadWrite);
Require(sceKernelFcntl(pipe[1], GetDescriptorFlags) == 1);
Require(close_nid_postfix(pipe[0]) == 0 && close_nid_postfix(pipe[1]) == 0);
const int random = sceKernelOpen("/dev/urandom", ReadOnly, 0);
Require(random >= 0);
Require(fcntl_nid_postfix(socket, Duplicate2Command, random) == random);
Require(sceKernelFcntl(random, GetFlags) == ReadWrite);
Require(close_nid_postfix(random) == 0);
const int file = sceKernelOpen("kernel_fcntl_socket_dup2.bin", CreateReadWrite, 0644);
Require(file == random);
char byte = 0;
Require(sceKernelRead(file, &byte, 1) == 0);
Require(sceKernelFcntl(socket, Duplicate2CloseOnExec, file) == file);
Require(sceKernelFcntl(file, GetFlags) == ReadWrite);
Require(sceKernelFcntl(file, GetDescriptorFlags) == 1);
Require(close_nid_postfix(file) == 0);
std::remove("kernel_fcntl_socket_dup2.bin");
RequireFailure(fcntl_nid_postfix(socket, DuplicateCommand, -1), GuestEinval);
RequireFailure(fcntl_nid_postfix(socket, DuplicateCloseOnExec, -1), GuestEinval);
RequireFailure(fcntl_nid_postfix(socket, Duplicate2Command, -1), GuestEbadf);
RequireFailure(fcntl_nid_postfix(socket, Duplicate2CloseOnExec, -1), GuestEbadf);
Require(sceKernelFcntl(socket, DuplicateCommand, -1) == SceEinval);
Require(sceKernelFcntl(socket, DuplicateCloseOnExec, -1) == SceEinval);
Require(sceKernelFcntl(socket, Duplicate2Command, -1) == SceEbadf);
Require(sceKernelFcntl(socket, Duplicate2CloseOnExec, -1) == SceEbadf);
Require(sceKernelFcntl(socket, SetFlags, 0x8000) == SceEnotsup);
Require(sceKernelFcntl(socket, GetFlags) == ReadWrite);
Require(sceKernelFcntl(socket, 5) == SceEinval);
Require(sceKernelFcntl(socket + 4096, GetFlags) == SceEbadf);
int queued = -1;
Require(ioctl_nid_postfix(socket, ReadableRequest, &queued) == 0 && queued == 0);
Require(close_nid_postfix(socket) == 0);
for (const int descriptor : {closing, duplicate, socket}) Require(close_nid_postfix(descriptor) == 0);
Require(sceKernelFcntl(socket, GetFlags) == SceEbadf);
Require(sceKernelFcntl(-1, GetFlags) == SceEbadf);
Require(sceKernelFcntl(12345, GetFlags) == SceEbadf);
Expand Down
48 changes: 48 additions & 0 deletions core/libs/tests/GuestSocketDescriptorFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@ int APS5_VABI socket_nid_postfix(int, int, int);
int APS5_VABI close_nid_postfix(int);
int APS5_VABI dup_nid_postfix(int);
int APS5_VABI dup2_nid_postfix(int, int);
int APS5_VABI pipe_nid_postfix(int*);
int APS5_VABI fcntl_nid_postfix(int, int, ...);
int* APS5_VABI __error_nid_postfix();
}

namespace {

constexpr int DuplicateDescriptor = 0;
constexpr int GetDescriptorFlags = 1;
constexpr int SetDescriptorFlags = 2;
constexpr int Duplicate2Descriptor = 10;
constexpr int DuplicateDescriptorCloseOnExec = 17;
constexpr int Duplicate2DescriptorCloseOnExec = 18;
constexpr int CloseOnExec = 1;
constexpr int Stream = 1;
constexpr int SocketCloseOnExec = 0x10000000;
constexpr int GuestEbadf = 9;
constexpr int GuestEinval = 22;

void Require(bool condition, const char* what) {
if (condition) return;
Expand Down Expand Up @@ -51,6 +58,47 @@ int main() {
Require(fcntl_nid_postfix(plain, SetDescriptorFlags, 0) == 0, "F_SETFD 0 must clear FD_CLOEXEC");
Require(close_nid_postfix(target) == 0, "close failed");

Require(fcntl_nid_postfix(plain, SetDescriptorFlags, CloseOnExec) == 0, "F_SETFD on a socket must succeed");
const int minimumDup = fcntl_nid_postfix(plain, DuplicateDescriptor, plain + 4);
Require(minimumDup >= plain + 4 && fcntl_nid_postfix(minimumDup, GetDescriptorFlags) == 0, "F_DUPFD must honor the minimum and clear FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, GetDescriptorFlags) == CloseOnExec, "F_DUPFD must leave the flag of the original");
Require(fcntl_nid_postfix(plain, SetDescriptorFlags, 0) == 0, "F_SETFD 0 must clear FD_CLOEXEC");
const int cloexecDup = fcntl_nid_postfix(plain, DuplicateDescriptorCloseOnExec, minimumDup + 1);
Require(cloexecDup >= minimumDup + 1 && fcntl_nid_postfix(cloexecDup, GetDescriptorFlags) == CloseOnExec, "F_DUPFD_CLOEXEC must honor the minimum and set FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, GetDescriptorFlags) == 0, "F_DUPFD_CLOEXEC must leave the flag of the original");
Require(fcntl_nid_postfix(plain, DuplicateDescriptor, -1) == -1 && *__error_nid_postfix() == GuestEinval, "F_DUPFD with a negative minimum must fail with EINVAL");
Require(fcntl_nid_postfix(plain, DuplicateDescriptorCloseOnExec, -1) == -1 && *__error_nid_postfix() == GuestEinval, "F_DUPFD_CLOEXEC with a negative minimum must fail with EINVAL");
Require(close_nid_postfix(cloexecDup) == 0 && close_nid_postfix(minimumDup) == 0, "close failed");

const int dup2Target = socket_nid_postfix(2, Stream, 0);
Require(dup2Target >= 0, "socket failed");
Require(fcntl_nid_postfix(dup2Target, SetDescriptorFlags, CloseOnExec) == 0 && fcntl_nid_postfix(plain, SetDescriptorFlags, CloseOnExec) == 0, "F_SETFD on a socket must succeed");
Require(fcntl_nid_postfix(plain, Duplicate2Descriptor, dup2Target) == dup2Target, "F_DUP2FD onto another socket must return the target");
Require(fcntl_nid_postfix(dup2Target, GetDescriptorFlags) == 0, "F_DUP2FD must clear FD_CLOEXEC on the target");
Require(fcntl_nid_postfix(plain, GetDescriptorFlags) == CloseOnExec, "F_DUP2FD must leave the flag of the original");
Require(fcntl_nid_postfix(plain, Duplicate2Descriptor, plain) == plain && fcntl_nid_postfix(plain, GetDescriptorFlags) == CloseOnExec, "F_DUP2FD onto the same descriptor must preserve FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, SetDescriptorFlags, 0) == 0, "F_SETFD 0 must clear FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, Duplicate2Descriptor, plain) == plain && fcntl_nid_postfix(plain, GetDescriptorFlags) == 0, "F_DUP2FD onto the same descriptor must preserve a cleared flag");
Require(fcntl_nid_postfix(plain, Duplicate2DescriptorCloseOnExec, dup2Target) == dup2Target, "F_DUP2FD_CLOEXEC onto another socket must return the target");
Require(fcntl_nid_postfix(dup2Target, GetDescriptorFlags) == CloseOnExec, "F_DUP2FD_CLOEXEC must set FD_CLOEXEC on the target");
Require(fcntl_nid_postfix(plain, GetDescriptorFlags) == 0, "F_DUP2FD_CLOEXEC must leave the flag of the original");
Require(fcntl_nid_postfix(plain, Duplicate2DescriptorCloseOnExec, plain) == plain && fcntl_nid_postfix(plain, GetDescriptorFlags) == CloseOnExec, "F_DUP2FD_CLOEXEC onto the same descriptor must set FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, SetDescriptorFlags, 0) == 0, "F_SETFD 0 must clear FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, Duplicate2Descriptor, -1) == -1 && *__error_nid_postfix() == GuestEbadf, "F_DUP2FD with a negative target must fail with EBADF");
Require(fcntl_nid_postfix(plain, Duplicate2DescriptorCloseOnExec, -1) == -1 && *__error_nid_postfix() == GuestEbadf, "F_DUP2FD_CLOEXEC with a negative target must fail with EBADF");
Require(close_nid_postfix(dup2Target) == 0, "close failed");

int pipeDescriptors[2] = {-1, -1};
Require(pipe_nid_postfix(pipeDescriptors) == 0, "pipe failed");
Require(fcntl_nid_postfix(pipeDescriptors[0], SetDescriptorFlags, CloseOnExec) == 0, "F_SETFD on a pipe must succeed");
Require(fcntl_nid_postfix(plain, SetDescriptorFlags, CloseOnExec) == 0, "F_SETFD on a socket must succeed");
Require(fcntl_nid_postfix(plain, Duplicate2Descriptor, pipeDescriptors[0]) == pipeDescriptors[0], "F_DUP2FD onto a pipe must return the target");
Require(fcntl_nid_postfix(pipeDescriptors[0], GetDescriptorFlags) == 0, "F_DUP2FD onto a pipe must clear FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, SetDescriptorFlags, 0) == 0, "F_SETFD 0 must clear FD_CLOEXEC");
Require(fcntl_nid_postfix(plain, Duplicate2DescriptorCloseOnExec, pipeDescriptors[1]) == pipeDescriptors[1], "F_DUP2FD_CLOEXEC onto a pipe must return the target");
Require(fcntl_nid_postfix(pipeDescriptors[1], GetDescriptorFlags) == CloseOnExec, "F_DUP2FD_CLOEXEC onto a pipe must set FD_CLOEXEC");
Require(close_nid_postfix(pipeDescriptors[0]) == 0 && close_nid_postfix(pipeDescriptors[1]) == 0, "close failed");

const int marked = socket_nid_postfix(2, Stream | SocketCloseOnExec, 0);
Require(marked >= 0 && fcntl_nid_postfix(marked, GetDescriptorFlags) == CloseOnExec, "SOCK_CLOEXEC must set FD_CLOEXEC");
Require(close_nid_postfix(marked) == 0, "close failed");
Expand Down
Loading
Loading