mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
Merge pull request #11135 from orezraey/fix/libkernel-dupfd-cloexec-measured-ps5
fix(libkernel): fail F_DUPFD_CLOEXEC and make F_DUP2FD_CLOEXEC ignore its target as measured on a PS5
This commit is contained in:
6 files changed
+81
-56
No files matched your search
@@ -45,6 +45,7 @@ constexpr int GuestEfault = 14;
|
||||
constexpr int GuestEinval = 22;
|
||||
constexpr int GuestEnotty = 25;
|
||||
constexpr int GuestEagain = 35;
|
||||
constexpr int GuestEopnotsupp = 45;
|
||||
constexpr int GuestEnolck = 77;
|
||||
constexpr int GuestEoverflow = 84;
|
||||
|
||||
@@ -489,21 +490,20 @@ FcntlArgument FcntlArgumentOf(int command) {
|
||||
|
||||
int Fcntl(int fd, int command, std::intptr_t argument) {
|
||||
switch (command) {
|
||||
case DuplicateCommand:
|
||||
case DuplicateCloseOnExec: {
|
||||
case DuplicateCommand: {
|
||||
if (fd < 0) return Fail(GuestEbadf);
|
||||
if (static_cast<int>(argument) < 0) return Fail(GuestEinval);
|
||||
if (!Classify(fd, "fcntl")) return Fail(GuestEbadf);
|
||||
const int duplicate = DuplicateFrom(fd, static_cast<int>(argument));
|
||||
if (duplicate >= 0 && command == DuplicateCloseOnExec) SetCloseOnExec(duplicate, true);
|
||||
return duplicate;
|
||||
return DuplicateFrom(fd, static_cast<int>(argument));
|
||||
}
|
||||
case DuplicateCloseOnExec:
|
||||
if (!Classify(fd, "fcntl")) return Fail(GuestEbadf);
|
||||
return Fail(GuestEopnotsupp);
|
||||
case DuplicateToCommand:
|
||||
case DuplicateToCloseOnExec: {
|
||||
const int duplicate = dup2_nid_postfix(fd, static_cast<int>(argument));
|
||||
if (duplicate >= 0 && command == DuplicateToCloseOnExec) SetCloseOnExec(duplicate, true);
|
||||
return duplicate;
|
||||
}
|
||||
return dup2_nid_postfix(fd, static_cast<int>(argument));
|
||||
case DuplicateToCloseOnExec:
|
||||
if (fd < 0 || !Classify(fd, "fcntl")) return Fail(GuestEbadf);
|
||||
return DuplicateFrom(fd, 0);
|
||||
case GetDescriptorFlags: {
|
||||
if (!Classify(fd, "fcntl")) return Fail(GuestEbadf);
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
@@ -667,7 +667,7 @@ bool GuestSockets::PipeReady(int descriptor, short events, short& revents) {
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
int DuplicateSocket_nid_no_patch(int descriptor, int minimum, bool closeOnExec) {
|
||||
int DuplicateSocket_nid_no_patch(int descriptor, int minimum) {
|
||||
if (minimum < 0) return Fail(22);
|
||||
std::lock_guard lock(socketsMutex);
|
||||
const auto found = sockets.find(descriptor);
|
||||
@@ -676,8 +676,7 @@ int DuplicateSocket_nid_no_patch(int descriptor, int minimum, bool closeOnExec)
|
||||
if (duplicate < 0) return -1;
|
||||
try {
|
||||
sockets.insert_or_assign(duplicate, found->second);
|
||||
if (closeOnExec) closeOnExecDescriptors.insert(duplicate);
|
||||
else closeOnExecDescriptors.erase(duplicate);
|
||||
closeOnExecDescriptors.erase(duplicate);
|
||||
} catch (const std::bad_alloc&) {
|
||||
sockets.erase(duplicate);
|
||||
ReleaseNumber(duplicate);
|
||||
@@ -688,7 +687,7 @@ int DuplicateSocket_nid_no_patch(int descriptor, int minimum, bool closeOnExec)
|
||||
}
|
||||
|
||||
int GuestSockets::Duplicate(int descriptor) {
|
||||
return DuplicateSocket_nid_no_patch(descriptor, 0, false);
|
||||
return DuplicateSocket_nid_no_patch(descriptor, 0);
|
||||
}
|
||||
|
||||
int GuestSockets::DuplicateTo(int descriptor, int target) {
|
||||
@@ -1116,12 +1115,10 @@ int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (command == 0) return DuplicateSocket_nid_no_patch(descriptor, static_cast<int>(argument));
|
||||
if (command == 17) return Fail(45);
|
||||
if (command == 10) return dup2_nid_postfix(descriptor, static_cast<int>(argument));
|
||||
if (command == 18) return DuplicateSocket_nid_no_patch(descriptor, 0);
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
std::lock_guard lock(socket->modeMutex);
|
||||
|
||||
@@ -55,6 +55,7 @@ static constexpr int GuestEfault = 14;
|
||||
static constexpr int GuestEinval = 22;
|
||||
static constexpr int GuestEnotty = 25;
|
||||
static constexpr int GuestEagain = 35;
|
||||
static constexpr int GuestEopnotsupp = 45;
|
||||
static constexpr int GuestEoverflow = 84;
|
||||
static constexpr std::uint64_t NonblockingRequest = 0x8004667e;
|
||||
static constexpr std::uint64_t ReadableRequest = 0x4004667f;
|
||||
@@ -121,18 +122,27 @@ static void SocketsKeepTheirPath() {
|
||||
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(sceKernelFcntl(socket, DuplicateCloseOnExec, duplicate + 1) == SceEnotsup);
|
||||
RequireFailure(fcntl_nid_postfix(socket, DuplicateCloseOnExec, duplicate + 1), GuestEopnotsupp);
|
||||
const int closing = sceKernelFcntl(socket, DuplicateCommand, duplicate + 1);
|
||||
Require(closing >= duplicate + 1);
|
||||
Require(sceKernelFcntl(closing, SetDescriptorFlags, 1) == 0);
|
||||
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);
|
||||
for (const int target : {closing, socket, -1, -2}) {
|
||||
const int copy = sceKernelFcntl(socket, Duplicate2CloseOnExec, target);
|
||||
Require(copy >= 0 && copy != target && copy != socket && copy != closing);
|
||||
Require(sceKernelFcntl(copy, GetDescriptorFlags) == 0 && sceKernelFcntl(copy, GetFlags) == ReadWrite);
|
||||
Require(close_nid_postfix(copy) == 0);
|
||||
const int posix = fcntl_nid_postfix(socket, Duplicate2CloseOnExec, target);
|
||||
Require(posix >= 0 && posix != target && posix != socket && close_nid_postfix(posix) == 0);
|
||||
}
|
||||
Require(sceKernelFcntl(closing, GetDescriptorFlags) == 0);
|
||||
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 0);
|
||||
Require(sceKernelFcntl(socket, Duplicate2CloseOnExec, socket) == socket);
|
||||
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 1);
|
||||
Require(sceKernelFcntl(socket, SetDescriptorFlags, 1) == 0);
|
||||
Require(sceKernelFcntl(socket, Duplicate2Command, socket) == socket);
|
||||
Require(sceKernelFcntl(socket, GetDescriptorFlags) == 1);
|
||||
Require(sceKernelFcntl(socket, SetDescriptorFlags, 0) == 0);
|
||||
@@ -142,10 +152,19 @@ static void SocketsKeepTheirPath() {
|
||||
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);
|
||||
const int pipeCopy = sceKernelFcntl(socket, Duplicate2CloseOnExec, pipe[1]);
|
||||
Require(pipeCopy >= 0 && pipeCopy != pipe[1] && sceKernelFcntl(pipeCopy, GetDescriptorFlags) == 0 && close_nid_postfix(pipeCopy) == 0);
|
||||
Require(sceKernelFcntl(pipe[1], GetDescriptorFlags) == 0);
|
||||
for (const int end : pipe) {
|
||||
const int copy = fcntl_nid_postfix(end, Duplicate2CloseOnExec, 300);
|
||||
Require(copy >= 0 && copy != end && copy != 300 && fcntl_nid_postfix(copy, GetDescriptorFlags) == 0 && close_nid_postfix(copy) == 0);
|
||||
}
|
||||
for (const int end : pipe) RequireFailure(fcntl_nid_postfix(end, DuplicateCloseOnExec, 0), GuestEopnotsupp);
|
||||
Require(close_nid_postfix(pipe[0]) == 0 && close_nid_postfix(pipe[1]) == 0);
|
||||
const int shared = shm_open_nid_postfix(reinterpret_cast<const char*>(1), ReadWrite, 0600);
|
||||
Require(shared >= 0);
|
||||
RequireFailure(fcntl_nid_postfix(shared, DuplicateCloseOnExec, 0), GuestEopnotsupp);
|
||||
Require(close_nid_postfix(shared) == 0);
|
||||
const int random = sceKernelOpen("/dev/urandom", ReadOnly, 0);
|
||||
Require(random >= 0);
|
||||
Require(fcntl_nid_postfix(socket, Duplicate2Command, random) == random);
|
||||
@@ -155,19 +174,22 @@ static void SocketsKeepTheirPath() {
|
||||
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);
|
||||
const int fileCopy = sceKernelFcntl(file, Duplicate2CloseOnExec, 300);
|
||||
Require(fileCopy >= 0 && fileCopy != file && fileCopy != 300);
|
||||
Require(sceKernelFcntl(fileCopy, GetDescriptorFlags) == 0 && sceKernelFcntl(fileCopy, GetFlags) == ReadWrite);
|
||||
Require(sceKernelLseek(file, 1, 0) == 1 && sceKernelLseek(fileCopy, 0, 1) == 1);
|
||||
Require(close_nid_postfix(fileCopy) == 0);
|
||||
RequireFailure(fcntl_nid_postfix(-1, Duplicate2CloseOnExec, 300), GuestEbadf);
|
||||
RequireFailure(fcntl_nid_postfix(9999, Duplicate2CloseOnExec, 300), GuestEbadf);
|
||||
Require(sceKernelFcntl(9999, Duplicate2CloseOnExec, 300) == SceEbadf);
|
||||
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, DuplicateCloseOnExec, -1), GuestEopnotsupp);
|
||||
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, DuplicateCloseOnExec, -1) == SceEnotsup);
|
||||
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);
|
||||
@@ -218,9 +240,13 @@ static void FileFlagsRoundTrip() {
|
||||
Require(fcntl_nid_postfix(duplicate, GetDescriptorFlags) == 0);
|
||||
Require(fcntl_nid_postfix(file, SetFlags, NonBlocking) == 0);
|
||||
Require(fcntl_nid_postfix(duplicate, GetFlags) == (ReadWrite | NonBlocking));
|
||||
const int closing = fcntl_nid_postfix(file, DuplicateCloseOnExec, duplicate);
|
||||
Require(closing > duplicate);
|
||||
Require(fcntl_nid_postfix(closing, GetDescriptorFlags) == 1);
|
||||
for (const int minimum : {0, duplicate, 200, -1}) {
|
||||
RequireFailure(fcntl_nid_postfix(file, DuplicateCloseOnExec, minimum), GuestEopnotsupp);
|
||||
Require(sceKernelFcntl(file, DuplicateCloseOnExec, minimum) == SceEnotsup);
|
||||
}
|
||||
RequireFailure(fcntl_nid_postfix(-1, DuplicateCloseOnExec, 0), GuestEbadf);
|
||||
RequireFailure(fcntl_nid_postfix(9999, DuplicateCloseOnExec, 0), GuestEbadf);
|
||||
Require(sceKernelFcntl(-1, DuplicateCloseOnExec, 0) == SceEbadf);
|
||||
Require(fcntl_nid_postfix(file, GetDescriptorFlags) == 0);
|
||||
|
||||
Require(write_nid_postfix(file, "abcde", 5) == 5);
|
||||
@@ -250,7 +276,7 @@ static void FileFlagsRoundTrip() {
|
||||
lock.type = 7;
|
||||
RequireFailure(fcntl_nid_postfix(file, SetLock, &lock), GuestEinval);
|
||||
|
||||
for (const int descriptor : {closing, duplicate, file}) Require(close_nid_postfix(descriptor) == 0);
|
||||
for (const int descriptor : {duplicate, file}) Require(close_nid_postfix(descriptor) == 0);
|
||||
RequireFailure(fcntl_nid_postfix(file, GetDescriptorFlags), GuestEbadf);
|
||||
RequireFailure(fcntl_nid_postfix(file, DuplicateCommand, 0), GuestEbadf);
|
||||
RequireFailure(ioctl_nid_postfix(file, ReadableRequest, &readable), GuestEbadf);
|
||||
|
||||
@@ -18,6 +18,7 @@ constexpr int Stream = 1;
|
||||
constexpr int SocketCloseOnExec = 0x10000000;
|
||||
constexpr int GuestEbadf = 9;
|
||||
constexpr int GuestEinval = 22;
|
||||
constexpr int GuestEopnotsupp = 45;
|
||||
|
||||
void Require(bool condition, const char* what) {
|
||||
if (condition) return;
|
||||
@@ -55,12 +56,11 @@ int main() {
|
||||
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, DuplicateDescriptorCloseOnExec, minimumDup + 1) == -1 && *__error_nid_postfix() == GuestEopnotsupp, "F_DUPFD_CLOEXEC must fail with EOPNOTSUPP");
|
||||
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");
|
||||
Require(fcntl_nid_postfix(plain, DuplicateDescriptorCloseOnExec, -1) == -1 && *__error_nid_postfix() == GuestEopnotsupp, "F_DUPFD_CLOEXEC with a negative minimum must fail with EOPNOTSUPP");
|
||||
Require(close_nid_postfix(minimumDup) == 0, "close failed");
|
||||
|
||||
const int dup2Target = socket_nid_postfix(2, Stream, 0);
|
||||
Require(dup2Target >= 0, "socket failed");
|
||||
@@ -71,13 +71,14 @@ int main() {
|
||||
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");
|
||||
for (const int target : {dup2Target, plain, -1}) {
|
||||
const int copy = fcntl_nid_postfix(plain, Duplicate2DescriptorCloseOnExec, target);
|
||||
Require(copy >= 0 && copy != target && copy != plain, "F_DUP2FD_CLOEXEC must ignore the target and return a new descriptor");
|
||||
Require(fcntl_nid_postfix(copy, GetDescriptorFlags) == 0, "F_DUP2FD_CLOEXEC must not set FD_CLOEXEC");
|
||||
Require(close_nid_postfix(copy) == 0, "close failed");
|
||||
}
|
||||
Require(fcntl_nid_postfix(dup2Target, GetDescriptorFlags) == 0 && fcntl_nid_postfix(plain, GetDescriptorFlags) == 0, "F_DUP2FD_CLOEXEC must leave the target and the original alone");
|
||||
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};
|
||||
@@ -87,8 +88,9 @@ int main() {
|
||||
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");
|
||||
const int pipeCopy = fcntl_nid_postfix(plain, Duplicate2DescriptorCloseOnExec, pipeDescriptors[1]);
|
||||
Require(pipeCopy >= 0 && pipeCopy != pipeDescriptors[1] && fcntl_nid_postfix(pipeCopy, GetDescriptorFlags) == 0, "F_DUP2FD_CLOEXEC must leave a pipe target alone");
|
||||
Require(close_nid_postfix(pipeCopy) == 0 && fcntl_nid_postfix(pipeDescriptors[1], GetDescriptorFlags) == 0, "close failed");
|
||||
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);
|
||||
|
||||
@@ -177,8 +177,7 @@ static void SocketNumbersStayInDescriptorSet() {
|
||||
Require(dup_nid_postfix(socket) == -1 && *__error_nid_postfix() == 24);
|
||||
Require(fcntl_nid_postfix(socket, DuplicateCommand, 0) == -1 && *__error_nid_postfix() == 24);
|
||||
Require(fcntl_nid_postfix(socket, DuplicateCommand, DescriptorSetSize) == -1 && *__error_nid_postfix() == 24);
|
||||
Require(fcntl_nid_postfix(socket, DuplicateCloseOnExec, 0) == -1 && *__error_nid_postfix() == 24);
|
||||
Require(fcntl_nid_postfix(socket, DuplicateCloseOnExec, DescriptorSetSize) == -1 && *__error_nid_postfix() == 24);
|
||||
Require(fcntl_nid_postfix(socket, DuplicateCloseOnExec, 0) == -1 && *__error_nid_postfix() == 45);
|
||||
Require(socket_nid_postfix(Inet, Datagram, 0) == -1 && *__error_nid_postfix() == 24);
|
||||
Require(dup2_nid_postfix(socket, DescriptorSetSize) == -1 && *__error_nid_postfix() == 24);
|
||||
for (const int file : files) Require(close_nid_postfix(file) == 0);
|
||||
|
||||
@@ -268,10 +268,11 @@
|
||||
- [sigaction](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's `sigaction` (`EINVAL` for other signals, and for a handler on `SIGKILL` or `SIGSTOP`); `signal` records its handler with `SA_RESTART`, as FreeBSD's libc does. Only handlers without `SA_SIGINFO` on the signals `signal` already delivers (`SIGINT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM`) run, the same way `signal` handlers do. While a handler runs, its own signal (unless `SA_NODEFER`) and the signals of its mask other than `SIGKILL` and `SIGSTOP` are blocked, and `SA_RESETHAND` resets the disposition to the default before it runs, as FreeBSD's `postsig_done` does. Those blocks are held apart from the process-wide mask that `sigprocmask` sets, which reports them, and are released when the handler returns, so a block another thread adds meanwhile is kept, for the same signal too, and so is one the handler adds itself, which FreeBSD's `sigreturn` would undo. `SIG_UNBLOCK` of a held signal releases its hold and `SIG_SETMASK` releases them all. When the handler's own thread released one, that signal gets back its bit of the process mask from before the handler when the handler returns, as FreeBSD's `sigreturn` puts back the mask from before it, so a handler that saves and restores the mask with `SIG_SETMASK`, or calls `pselect`, leaves its signal unblocked. A `sigsuspend` inside a handler keeps the holds, where FreeBSD replaces the whole thread mask. A handler left with `longjmp`, or with `siglongjmp` to a buffer that saved the mask, gets back the mask that buffer saved, as on FreeBSD; a signal raised meanwhile stays pending and is delivered when the handler returns to `raise`, but one left pending by a handler that a host signal ran asynchronously waits for the next `raise` or `_sigprocmask`. `SA_ONSTACK` is not applied. `SA_SIGINFO` handlers are recorded but never called, since that needs FreeBSD's `siginfo_t` and `ucontext_t`, and neither are the handlers of other signals (`SIGBUS`, `SIGHUP`, ...), whether they are set by `sigaction` or by `signal`, which records them too (every number from 1 to 128, with `SA_RESTART` and an empty mask replacing what `sigaction` stored; `SIGKILL` and `SIGSTOP` accept only the default handler in both calls, as in FreeBSD's `kern_sigaction`). `raise` and `kill` accept every number from 1 to 128 now: a blocked one stays pending on the set of `sigtimedwait`, an unblocked one whose handler is registered runs it on the calling thread, and an unblocked one that has no host equivalent and no handler still fails with `EINVAL`, where FreeBSD runs its default action. `raise` of an `SA_SIGINFO` handler still throws instead of terminating
|
||||
- [signal](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records its handler with `SA_RESTART` unless the signal is in the exported `_sigintr` set, as FreeBSD's libc does (`lib/libc/gen/signal.c`). `siginterrupt`, which fills that set, is a libSceLibcInternal export that libc serves through the `sigaction` and the set libkernel registers at load (see libc).
|
||||
- [kill, killpg](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) follow FreeBSD's `kern_kill` and `killpg1` for the only guest process: `EINVAL` for signals outside 0 to 128 and for negative `killpg` process groups, signal 0 only checks that the target exists, and its own pid, 0 and minus its own pid (the guest is taken to lead its own process group) signal it through `raise`, so they share `raise`'s limits above. Any other pid, and -1, which skips the caller, report `ESRCH`, so `EPERM` is never reported
|
||||
- [sceKernelFcntl](../../../core/libs/prx/libkernel/File/src/Open.cpp) (libkernel) runs the POSIX `fcntl` below and returns its guest errno as `SCE_KERNEL_ERROR_*`, the form the other `sceKernel` file exports use. The return values and error codes are unverified on a console
|
||||
- [sceKernelFcntl](../../../core/libs/prx/libkernel/File/src/Open.cpp) (libkernel) runs the POSIX `fcntl` below and returns its guest errno as `SCE_KERNEL_ERROR_*`, the form the other `sceKernel` file exports use. On PS5 firmware 13.60 it returns what `fcntl` returns there, and `0x80020000` plus the errno, which it also sets, on failure; this was measured for `F_GETFD`, `F_SETFD`, `F_GETFL`, `F_DUPFD`, `F_DUPFD_CLOEXEC`, `F_GETLK`, an unknown command and bad descriptors, not for the other commands
|
||||
- [Creating through a trailing slash](../../../core/libs/prx/libkernel/File/src/Open.cpp) (libkernel) - `open` and `sceKernelOpen` with `O_CREAT` on a missing name followed by a slash (`new/`) fail with `EINVAL` on Windows and with the host's `EISDIR` on Linux, where FreeBSD 11.4's `lookup()` (sys/kern/vfs_lookup.c) fails with `ENOENT` because only a directory may be created through a trailing slash
|
||||
- [fcntl](../../../core/libs/prx/libkernel/File/src/DescriptorControl.cpp) (libkernel) follows FreeBSD 11.4's [`kern_fcntl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L479-L799) and [`kern_dup`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L812) on file, directory, pipe and character device descriptors; a socket keeps the libSceNet path (`F_DUPFD`, `F_DUP2FD`, `F_DUPFD_CLOEXEC`, `F_DUP2FD_CLOEXEC`, `F_GETFL`, `F_SETFL` with `O_NONBLOCK`, `F_GETFD` and `F_SETFD`, `EINVAL` for every other command, see the next entry). On Linux and macOS a host socket that is not a guest socket, such as a standard descriptor a service manager connected to its journal, takes the flag handling of a pipe end and `FIONREAD` asks the host for its queued bytes (for a datagram socket Linux counts only the next datagram, where FreeBSD's [`soo_ioctl`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/sys_socket.c#L202-L205) counts every buffered byte); its record locks and read-ahead hints fail with `EBADF`, as FreeBSD fails them on a descriptor without a vnode, and `O_ASYNC` on it throws. The close-on-exec flag and the status flags live in a guest table, the status flags shared by `dup`, `dup2` and `F_DUPFD` as in an open file description; a descriptor the table has not seen, such as 0 to 2, starts from the host's flags, since the console's are not known. `F_DUPFD` and `F_DUPFD_CLOEXEC` return the lowest free host descriptor at or above the start, so the bound is the host table (`EMFILE` on Windows, `EINVAL` past `RLIMIT_NOFILE` on Linux), not the console's unknown `RLIMIT_NOFILE` and `kern.maxfilesperproc`. `O_NONBLOCK` switches a host pipe (`PIPE_NOWAIT` on Windows, where an empty read and a full write then report `EAGAIN`) and is only recorded on files, directories and the random device, as their reads never block; on a Windows console or other character device it throws. `O_ASYNC` on a pipe or character device (`SIGIO` is not delivered), `F_GETOWN`, `F_SETOWN` and `F_SETLK_REMOTE` throw; `O_FSYNC` set by `open` or `F_SETFL` makes each `write`, `pwrite`, `writev` and `pwritev` on a file sync it before returning, as `vn_write` adds `IO_SYNC`; the `sceKernelAioSubmitWriteCommands` writes are not synced, as whether the console's AIO honours the flag is unknown; on Linux a file opened with it keeps the host `O_SYNC` after `F_SETFL` clears it. `O_DIRECT`, `F_RDAHEAD` and `F_READAHEAD` are recorded as the hints they are. Record locks (`F_GETLK`, `F_SETLK`, `F_SETLKW` and their forms without `l_sysid`) use the host's POSIX locks on Linux and macOS. `F_GETLK` with `F_UNLCK` asks the host with `F_RDLCK`: as in [`lf_blocks`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_lockf.c#L866) only another owner's overlapping write lock conflicts with either request. On Windows they follow [`kern_fcntl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L607-L752) and [`lf_advlockasync`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_lockf.c#L433-L487) for the one guest process, which owns every lock: `EOVERFLOW` for an `SEEK_CUR` start past `OFF_MAX`, then `EBADF` for `F_RDLCK` without read access or `F_WRLCK` without write access and `EINVAL` for another type, then `EINVAL` and `EOVERFLOW` for the range; as [`lf_blocks`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_lockf.c#L866) never lets locks of one owner conflict, whichever descriptor or thread asks, `F_SETLK` and `F_SETLKW` then succeed without waiting and `F_GETLK` reports `F_UNLCK`, so no lock list is kept and merging, splitting and the release on close have nothing to change. Nothing is held on the host, since Windows byte-range locks are mandatory and would fail the guest's own reads and writes through other descriptors, so other host processes neither see these locks nor are seen; a lock from the end of a directory throws. The return values and error codes are unverified on a console
|
||||
- [fcntl on a guest socket](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answers `F_DUPFD`, `F_DUP2FD`, `F_DUPFD_CLOEXEC`, `F_DUP2FD_CLOEXEC`, `F_GETFD`, `F_SETFD`, `F_GETFL` and `F_SETFL` and fails every other command with `EINVAL`; FreeBSD 11.4's `kern_fcntl` also handles `F_GETOWN` and `F_SETOWN` on a socket. `FD_CLOEXEC` is only recorded (there is no `exec`): `dup`, `dup2`, `F_DUPFD`, `F_DUP2FD` onto another descriptor and an accepted socket start without it, `F_DUP2FD` onto the same descriptor keeps it, and `SOCK_CLOEXEC`, `F_DUPFD_CLOEXEC`, `F_DUP2FD_CLOEXEC` and `F_SETFD` set it. None of this was measured on a console.
|
||||
- [fcntl](../../../core/libs/prx/libkernel/File/src/DescriptorControl.cpp) (libkernel) follows FreeBSD 11.4's [`kern_fcntl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L479-L799) and [`kern_dup`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L812) on file, directory, pipe and character device descriptors; a socket keeps the libSceNet path (`F_DUPFD`, `F_DUP2FD`, `F_DUP2FD_CLOEXEC`, `F_GETFL`, `F_SETFL` with `O_NONBLOCK`, `F_GETFD` and `F_SETFD`, `EINVAL` for every other command, see the next entry). On Linux and macOS a host socket that is not a guest socket, such as a standard descriptor a service manager connected to its journal, takes the flag handling of a pipe end and `FIONREAD` asks the host for its queued bytes (for a datagram socket Linux counts only the next datagram, where FreeBSD's [`soo_ioctl`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/sys_socket.c#L202-L205) counts every buffered byte); its record locks and read-ahead hints fail with `EBADF`, as FreeBSD fails them on a descriptor without a vnode, and `O_ASYNC` on it throws. The close-on-exec flag and the status flags live in a guest table, the status flags shared by `dup`, `dup2` and `F_DUPFD` as in an open file description; a descriptor the table has not seen, such as 0 to 2, starts from the host's flags, since the console's are not known. `F_DUPFD` returns the lowest free host descriptor at or above the start, so the bound is the host table (`EMFILE` on Windows, `EINVAL` past `RLIMIT_NOFILE` on Linux), not the console's unknown `RLIMIT_NOFILE` and `kern.maxfilesperproc`. `O_NONBLOCK` switches a host pipe (`PIPE_NOWAIT` on Windows, where an empty read and a full write then report `EAGAIN`) and is only recorded on files, directories and the random device, as their reads never block; on a Windows console or other character device it throws. `O_ASYNC` on a pipe or character device (`SIGIO` is not delivered), `F_GETOWN`, `F_SETOWN` and `F_SETLK_REMOTE` throw; `O_FSYNC` set by `open` or `F_SETFL` makes each `write`, `pwrite`, `writev` and `pwritev` on a file sync it before returning, as `vn_write` adds `IO_SYNC`; the `sceKernelAioSubmitWriteCommands` writes are not synced, as whether the console's AIO honours the flag is unknown; on Linux a file opened with it keeps the host `O_SYNC` after `F_SETFL` clears it. `O_DIRECT`, `F_RDAHEAD` and `F_READAHEAD` are recorded as the hints they are. Record locks (`F_GETLK`, `F_SETLK`, `F_SETLKW` and their forms without `l_sysid`) use the host's POSIX locks on Linux and macOS. `F_GETLK` with `F_UNLCK` asks the host with `F_RDLCK`: as in [`lf_blocks`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_lockf.c#L866) only another owner's overlapping write lock conflicts with either request. On Windows they follow [`kern_fcntl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_descrip.c#L607-L752) and [`lf_advlockasync`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_lockf.c#L433-L487) for the one guest process, which owns every lock: `EOVERFLOW` for an `SEEK_CUR` start past `OFF_MAX`, then `EBADF` for `F_RDLCK` without read access or `F_WRLCK` without write access and `EINVAL` for another type, then `EINVAL` and `EOVERFLOW` for the range; as [`lf_blocks`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/kern_lockf.c#L866) never lets locks of one owner conflict, whichever descriptor or thread asks, `F_SETLK` and `F_SETLKW` then succeed without waiting and `F_GETLK` reports `F_UNLCK`, so no lock list is kept and merging, splitting and the release on close have nothing to change. Nothing is held on the host, since Windows byte-range locks are mandatory and would fail the guest's own reads and writes through other descriptors, so other host processes neither see these locks nor are seen; a lock from the end of a directory throws. The return values and error codes are unverified on a console
|
||||
- [fcntl on a guest socket](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) answers `F_DUPFD`, `F_DUP2FD`, `F_DUP2FD_CLOEXEC`, `F_GETFD`, `F_SETFD`, `F_GETFL` and `F_SETFL`, fails `F_DUPFD_CLOEXEC` with `EOPNOTSUPP` as a PS5 does, and fails every other command with `EINVAL`; FreeBSD 11.4's `kern_fcntl` also handles `F_GETOWN` and `F_SETOWN` on a socket. `FD_CLOEXEC` is only recorded (there is no `exec`): `dup`, `dup2`, `F_DUPFD`, `F_DUP2FD` onto another descriptor and an accepted socket start without it, `F_DUP2FD` onto the same descriptor keeps it, and `SOCK_CLOEXEC` and `F_SETFD` set it. None of this was measured on a console.
|
||||
- [F_DUPFD_CLOEXEC and F_DUP2FD_CLOEXEC](../../../core/libs/prx/libkernel/File/src/DescriptorControl.cpp) (libkernel) - `F_DUPFD_CLOEXEC` on a shared memory descriptor fails with `EOPNOTSUPP`, where PS5 firmware 13.60 returns 0 for any start without setting `errno`; whether it duplicates anything is not known, since descriptor 0 was already open. `F_DUP2FD_CLOEXEC` returns the lowest free descriptor, where the console picks scattered free numbers below 1024 (17 to 1012 measured). Otherwise both follow the console: `F_DUPFD_CLOEXEC` fails with `EOPNOTSUPP` for any start on files, sockets, pipes and kqueues, after `EBADF` for a bad descriptor; `F_DUP2FD_CLOEXEC` ignores its target, which it neither opens nor closes (-1, -2, `INT_MAX`, the source and an open descriptor alike), and returns a new descriptor for the same open file without `FD_CLOEXEC`, or `EBADF` for a bad source
|
||||
- [ioctl, _ioctl](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) handle only `FIONBIO` and `FIONREAD` on a socket, and any other request there fails with `ENOTTY`. On the other descriptors they follow FreeBSD's [`kern_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_generic.c#L758), [`vn_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/vfs_vnops.c#L1488) and [`pipe_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_pipe.c#L1346): `FIONBIO` sets `O_NONBLOCK` as `fcntl` does, `FIOCLEX` and `FIONCLEX` set and clear the close-on-exec flag `F_GETFD` reports, and `FIONREAD` reports the size past the offset of a file, the queued bytes of a pipe's read end, 0 on its write end and `ENOTTY` on the random device, while it throws on a directory or another character device. Every termios request (group `t`, `TIOCGWINSZ` included) fails with `ENOTTY`, since those descriptors are taken as not a terminal, as in `tcgetattr`, and any other well-formed request throws. An input request with length 0 is not rejected as malformed: [`sys_ioctl`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_generic.c#L703-L718) rejects it only in a kernel built without `COMPAT_FREEBSD4`, `COMPAT_FREEBSD5` and `COMPAT_43`, and which of these the PS5 kernel has is unknown. Stremio Plus (PPSA74126) imports `_ioctl` for its statically linked `tc*` functions, which send `TIOCGETA`, `TIOCSETA`, `TIOCSPGRP` and the other termios requests
|
||||
- [ioctl, _ioctl](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) do not pass a `sceNetSocket` socket to libSceNet as `fcntl` does. On Linux its number duplicates the host socket, so `FIONBIO` switches the host `O_NONBLOCK` but not the library's `SO_NBIO` state; on Windows it is a `NUL` placeholder, so `FIONREAD` and enabling `FIONBIO` throw as on a character device. `sceNetIoctl` handles both requests on such a socket.
|
||||
- [_accept, _bind, _connect, _getpeername, _getsockname, _getsockopt, _listen, _recvfrom, _recvmsg, _sendmsg, _sendto, _setsockopt](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp), [_getdirentries](../../../core/libs/prx/libkernel/File/src/Stdio.cpp), [_sigaction, _sigsuspend](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) are not measured on the console. They forward to the calls without the underscore, which have no libthr layer here, as FreeBSD 11.4 makes the underscore names the plain system calls. `_sigaction` and `sigaction` share one handler table, where FreeBSD's `__thr_sigaction` hands its own wrapper to the system call `_sigaction` reads back, and rejects `SIGCANCEL` (32). A null set throws in `_sigsuspend` as in `sigsuspend`, while the system call FreeBSD 11.4 exports as `_sigsuspend` ([`Symbol.map`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/sys/Symbol.map#L961)) returns `EFAULT`
|
||||
|
||||
Reference in new issue
Block a user