Merge pull request #3455 from YossiYad/carry/select-non-socket-descriptors
fix(libkernel): answer select on non-socket descriptors
This commit is contained in:
commit
3a37a896fa
|
|
@ -2,6 +2,7 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/File/include/RandomDevice.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketPoll.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketSelectDeadline.hpp"
|
||||
|
||||
|
|
@ -9,6 +10,7 @@
|
|||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
|
@ -27,6 +29,11 @@ bool DescriptorSet(const void* set, int descriptor) {
|
|||
return set != nullptr && ((static_cast<const std::uint64_t*>(set)[descriptor / 64] >> (descriptor % 64)) & 1u) != 0;
|
||||
}
|
||||
|
||||
void UnknownDescriptor(int descriptor, bool polled) {
|
||||
const auto what = "select on descriptor " + std::to_string(descriptor) + (polled ? " (not a libSceNet socket)" : " (libSceNet is not loaded)");
|
||||
NotImplemented_nid_no_patch(what.c_str());
|
||||
}
|
||||
|
||||
void ClearDescriptor(void* set, int descriptor) {
|
||||
if (set != nullptr) static_cast<std::uint64_t*>(set)[descriptor / 64] &= ~(std::uint64_t{1} << (descriptor % 64));
|
||||
}
|
||||
|
|
@ -59,13 +66,20 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
|
|||
return -1;
|
||||
}
|
||||
std::vector<KernelSocketPoll::Entry> entries;
|
||||
std::vector<KernelSocketPoll::Entry> devices;
|
||||
for (int descriptor = 0; descriptor < nfds; ++descriptor) {
|
||||
short events = 0;
|
||||
if (DescriptorSet(readfds, descriptor)) events |= KernelSocketPoll::Readable;
|
||||
if (DescriptorSet(writefds, descriptor)) events |= KernelSocketPoll::Writable;
|
||||
if (DescriptorSet(exceptfds, descriptor)) events |= KernelSocketPoll::Urgent;
|
||||
if (events != 0) entries.push_back({descriptor, events, 0});
|
||||
if (events == 0) continue;
|
||||
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 entries.push_back({descriptor, events, 0});
|
||||
}
|
||||
const auto sockets = entries.size();
|
||||
const bool devicesReady = std::any_of(devices.begin(), devices.end(), [](const auto& entry) { return entry.revents != 0; });
|
||||
entries.insert(entries.end(), devices.begin(), devices.end());
|
||||
const auto start = std::chrono::steady_clock::now();
|
||||
const auto deadline = limit == nullptr ? std::chrono::steady_clock::time_point::max()
|
||||
: KernelSocketSelect::DeadlineAfter(start, limit->seconds, limit->microseconds);
|
||||
|
|
@ -76,34 +90,35 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
|
|||
const auto roundedMilliseconds = remaining > 0 ? remaining / 1000 + (remaining % 1000 != 0) : 0;
|
||||
waitMilliseconds = static_cast<int>(std::clamp<std::int64_t>(roundedMilliseconds, 0, 100));
|
||||
}
|
||||
if (!entries.empty()) {
|
||||
if (devicesReady) waitMilliseconds = 0;
|
||||
if (sockets != 0) {
|
||||
const auto poller = g_socketPoller.load();
|
||||
if (poller == nullptr) NotImplemented_nid_no_patch("select on descriptors that are not libSceNet sockets");
|
||||
for (auto& entry : entries) entry.revents = 0;
|
||||
const int result = poller(entries.data(), static_cast<int>(entries.size()), waitMilliseconds);
|
||||
if (poller == nullptr) UnknownDescriptor(entries.front().descriptor, false);
|
||||
for (std::size_t index = 0; index < sockets; ++index) entries[index].revents = 0;
|
||||
const int result = poller(entries.data(), static_cast<int>(sockets), waitMilliseconds);
|
||||
if (result < 0) {
|
||||
*__error_nid_postfix() = -result;
|
||||
return -1;
|
||||
}
|
||||
int ready = 0;
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & KernelSocketPoll::Unknown) != 0) NotImplemented_nid_no_patch("select on descriptors that are not libSceNet sockets");
|
||||
const bool readable = (entry.events & KernelSocketPoll::Readable) != 0 && (entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) != 0;
|
||||
const bool writable = (entry.events & KernelSocketPoll::Writable) != 0 && (entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) != 0;
|
||||
const bool urgent = (entry.events & KernelSocketPoll::Urgent) != 0 && (entry.revents & KernelSocketPoll::Urgent) != 0;
|
||||
ready += static_cast<int>(readable) + static_cast<int>(writable) + static_cast<int>(urgent);
|
||||
}
|
||||
if (ready > 0) {
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) == 0) ClearDescriptor(readfds, entry.descriptor);
|
||||
if ((entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) == 0) ClearDescriptor(writefds, entry.descriptor);
|
||||
if ((entry.revents & KernelSocketPoll::Urgent) == 0) ClearDescriptor(exceptfds, entry.descriptor);
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
} else if (waitMilliseconds > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(waitMilliseconds));
|
||||
}
|
||||
int ready = 0;
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & KernelSocketPoll::Unknown) != 0) UnknownDescriptor(entry.descriptor, true);
|
||||
const bool readable = (entry.events & KernelSocketPoll::Readable) != 0 && (entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) != 0;
|
||||
const bool writable = (entry.events & KernelSocketPoll::Writable) != 0 && (entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) != 0;
|
||||
const bool urgent = (entry.events & KernelSocketPoll::Urgent) != 0 && (entry.revents & KernelSocketPoll::Urgent) != 0;
|
||||
ready += static_cast<int>(readable) + static_cast<int>(writable) + static_cast<int>(urgent);
|
||||
}
|
||||
if (ready > 0) {
|
||||
for (const auto& entry : entries) {
|
||||
if ((entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) == 0) ClearDescriptor(readfds, entry.descriptor);
|
||||
if ((entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) == 0) ClearDescriptor(writefds, entry.descriptor);
|
||||
if ((entry.revents & KernelSocketPoll::Urgent) == 0) ClearDescriptor(exceptfds, entry.descriptor);
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
if (limit != nullptr && std::chrono::steady_clock::now() >= deadline) {
|
||||
for (const auto& entry : entries) {
|
||||
ClearDescriptor(readfds, entry.descriptor);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ int APS5_VABI sceNetResolverDestroy(int);
|
|||
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 close_nid_postfix(int);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
int APS5_VABI sceNetInetPton(int, const char*, void*);
|
||||
const char* APS5_VABI sceNetInetNtop(int, const void*, char*, std::uint32_t);
|
||||
|
|
@ -422,6 +424,24 @@ int main() {
|
|||
const std::int64_t bad_timeout[2]{0, 1000000};
|
||||
Require(select_nid_postfix(udp_receiver + 1, readable, nullptr, nullptr, bad_timeout) == -1 && *__error_nid_postfix() == 22);
|
||||
Require(select_nid_postfix(1025, nullptr, nullptr, nullptr, poll_now) == -1 && *__error_nid_postfix() == 22);
|
||||
const int random = open_nid_postfix("/dev/urandom", 0, 0);
|
||||
Require(random >= 0 && random < 1024);
|
||||
std::uint64_t random_readable[16]{};
|
||||
std::uint64_t random_urgent[16]{};
|
||||
random_readable[random / 64] |= std::uint64_t{1} << (random % 64);
|
||||
random_readable[udp_receiver / 64] |= std::uint64_t{1} << (udp_receiver % 64);
|
||||
random_urgent[random / 64] |= std::uint64_t{1} << (random % 64);
|
||||
Require(select_nid_postfix(std::max(random, udp_receiver) + 1, random_readable, nullptr, random_urgent, nullptr) == 1);
|
||||
Require(((random_readable[random / 64] >> (random % 64)) & 1u) != 0);
|
||||
Require(((random_readable[udp_receiver / 64] >> (udp_receiver % 64)) & 1u) == 0);
|
||||
Require(random_urgent[random / 64] == 0);
|
||||
Require(close_nid_postfix(random) == 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);
|
||||
Require(stdio_readable[0] == 0 && stdio_writable[0] == 6);
|
||||
stdio_readable[0] = 1;
|
||||
Require(select_nid_postfix(1, stdio_readable, nullptr, nullptr, poll_now) == 0 && stdio_readable[0] == 0);
|
||||
CheckMessages(udp_receiver, udp_sender, address);
|
||||
Require(sceNetSocketClose(udp_sender) == 0);
|
||||
Require(sceNetSocketClose(udp_receiver) == 0);
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [Dinkumware mutexes and conditions](../../core/libs/prx/libc/src/Threads.cpp) (libc) use pointer-to-handle arguments, matching the console aerolib signatures reported in [#3079](https://github.com/boykopovar/AnyPS5/pull/3079). The title's `_Mtx_lock` maps `0` to `_Thrd_success` (0), `0x8002000b` to `_Thrd_busy` (3), and other failures to `_Thrd_error` (4); its internal mutex init recognizes recursive flag `0x100`, while every other type uses default attributes. The observed relock failure for a plain mutex suggests those defaults are error-checking, but that has not been confirmed on hardware. The title's `_Mtx_unlock` and `_Cnd_wait` return success regardless of their internal result. The host implementation treats invalid ownership preconditions as no-ops, and throws for invalid handles, host wait errors and destroying a locked mutex as debug guards; those cases are not confirmed against the console.
|
||||
- [random, srandom, initstate, setstate](../../core/libs/prx/libc/src/Random.cpp) (libc) follow FreeBSD 11 [random.c](https://github.com/freebsd/freebsd-src/blob/4ab2e064d7950be84256d671a7ae93f87cc6aa36/lib/libc/stdlib/random.c): the additive feedback generator on a default 128-byte `TYPE_3` table, `good_rand` seeding, state sizes rounded down to 8, 32, 64, 128 or 256 bytes, and `setstate` returning the previous state or null for a rear position past the degree. `initstate` below 8 bytes returns null without setting errno, as that file and its manual do (glibc sets `EINVAL`). FreeBSD does not lock the generator; here every call is serialised by one lock, so concurrent callers draw the serial sequence between them. A null state throws, where FreeBSD faults. That the console's `libSceLibcInternal` uses this FreeBSD code and not another generator is unverified
|
||||
- [tcgetattr, tcsetattr](../../core/libs/prx/libkernel/File/src/Terminal.cpp) (libkernel) - every file and pipe descriptor is taken as not a terminal (`ENOTTY`), and a socket gets `EADDRNOTAVAIL` for `AF_INET` and `EOPNOTSUPP` otherwise, as FreeBSD's `in_control` and `pru_control_notsupp` answer a termios ioctl; what descriptors 0 to 2 of a title are on the console, and whether any of them is a terminal there, is unknown
|
||||
- [select](../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) - descriptor 0 is never reported readable and descriptors 1 and 2 are always writable, as for a console title with no input; reads from descriptor 0 still go to the host stdin. `/dev/random` and `/dev/urandom` are always readable and writable, as FreeBSD reports a seeded random device. Other descriptors that are not libSceNet sockets throw. Dungeons (PPSA16064) selects on descriptor 0
|
||||
- [__progname](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export
|
||||
- [environ](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - data export pointing to a fixed empty environment so titles that import it load; it is not the environment libc's `getenv` reads, so it lists none of those variables and `setenv`/`putenv` changes do not appear in it
|
||||
- [seteuid, setegid](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the guest process is assumed to run as root, with real, effective and saved user and group ids 0. As in FreeBSD's `sys_seteuid` and `sys_setegid`, id 0 is then accepted without a privilege check and changes no credential, so 0 is returned; any other id would change the effective id, which is not modelled, and throws. The ids and privileges a title has on a console are unverified
|
||||
|
|
|
|||
Loading…
Reference in New Issue