8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Scheduler.h"
13#include "pedigree/kernel/process/SignalEvent.h"
14#include "pedigree/kernel/process/Thread.h"
15#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
16#include "pedigree/kernel/processor/Processor.h"
17#include "pedigree/kernel/processor/VirtualAddressSpace.h"
18#include "pedigree/kernel/time/Time.h"
25#include "modules/subsys/posix/FileDescriptor.h"
26#include "modules/subsys/posix/PosixSubsystem.h"
27#include "modules/subsys/posix/eventfd-syscalls.h"
28#include "modules/subsys/posix/linux-wait-abi.h"
29#include "modules/subsys/posix/select-syscalls.h"
31#include "modules/system/vfs/Pipe.h"
38constexpr size_t BitsPerWord =
sizeof(uint64_t) * 8;
39constexpr size_t TestSignal = 10;
40constexpr int PreservedErrno = 123;
41constexpr uint64_t TestSignalBit =
static_cast<uint64_t
>(1) << (TestSignal - 1);
42constexpr uint64_t PreservedSignalBit =
static_cast<uint64_t
>(1) << (12 - 1);
43constexpr uint64_t UnblockableSignalBits =
44 (
static_cast<uint64_t
>(1) << (SIGKILL - 1)) | (
static_cast<uint64_t
>(1) << (SIGSTOP - 1));
45constexpr uint64_t OriginalSignalMask = TestSignalBit | PreservedSignalBit;
46constexpr uint64_t RequestedSignalMask = PreservedSignalBit | UnblockableSignalBits;
47constexpr uint64_t ActiveSignalMask = PreservedSignalBit;
53size_t bitmapExtent(
int nfds) {
54 return ((
static_cast<size_t>(nfds) + BitsPerWord - 1) / BitsPerWord) *
sizeof(uint64_t);
57void setBit(uint64_t* words,
size_t fd) {
58 words[fd / BitsPerWord] |=
static_cast<uint64_t
>(1) << (fd % BitsPerWord);
61bool isSet(
const uint64_t* words,
size_t fd) {
62 return words[fd / BitsPerWord] & (
static_cast<uint64_t
>(1) << (fd % BitsPerWord));
65void pselectSignalHandler(
size_t) {
66 g_PselectSignalHandlerCalls += 1;
67 if (g_PselectSignalWriter) {
69 if (g_PselectSignalWriter->
write(1,
reinterpret_cast<uintptr_t
>(&value),
true) == 1) {
70 g_PselectSignalHandlerWrites += 1;
85bool waitForPselectBlock(
Thread* thread) {
86 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
87 while (Time::getTicks() < deadline) {
89 uintptr_t debugAddress = 0;
99struct PselectValidationContext {
100 explicit PselectValidationContext(
size_t highReadFd)
101 : highReadFd(highReadFd), passed(false), returned(0) {}
108int pselectValidationWorker(
void* parameter) {
109 PselectValidationContext* context =
reinterpret_cast<PselectValidationContext*
>(parameter);
116 const int zeroResult = posix_pselect6(0,
nullptr,
nullptr,
nullptr, &zero,
nullptr);
117 passed &= zeroResult == 0 && thread->
getErrno() == PreservedErrno;
121 const int oneNanosecondResult =
122 posix_pselect6(0,
nullptr,
nullptr,
nullptr, &oneNanosecond,
nullptr);
123 passed &= oneNanosecondResult == 0 && !oneNanosecond.tv_sec && !oneNanosecond.tv_nsec &&
124 thread->
getErrno() == PreservedErrno;
128 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr, &negativeSeconds,
nullptr) == -1 &&
129 thread->
getErrno() == Error::InvalidArgument;
133 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr, &negativeNanoseconds,
nullptr) == -1 &&
134 thread->
getErrno() == Error::InvalidArgument;
138 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr, &excessiveNanoseconds,
nullptr) == -1 &&
139 thread->
getErrno() == Error::InvalidArgument;
141 uint64_t signalMask = 0;
144 sizeof(signalMask) - 1};
147 posix_pselect6(0,
nullptr,
nullptr,
nullptr, &unchangedWrongSize, &wrongMaskSize) == -1 &&
148 thread->
getErrno() == Error::InvalidArgument && unchangedWrongSize.tv_sec == 1 &&
149 unchangedWrongSize.tv_nsec == 123;
154 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr, &zero, &nullMask) == 0 &&
155 thread->
getErrno() == PreservedErrno;
161 posix_pselect6(0,
nullptr,
nullptr,
nullptr, &invalidWithBadArgument,
163 thread->
getErrno() == Error::BadAddress;
168 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr, &unchangedBadMask, &badMask) == -1 &&
169 thread->
getErrno() == Error::BadAddress && unchangedBadMask.tv_sec == 1 &&
170 unchangedBadMask.tv_nsec == 456;
174 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr, &invalidBeforeBadMask, &badMask) == -1 &&
175 thread->
getErrno() == Error::InvalidArgument;
178 passed &= posix_pselect6(0,
nullptr,
nullptr,
nullptr,
180 thread->
getErrno() == Error::BadAddress;
183 reinterpret_cast<uintptr_t
>(&RequestedSignalMask),
sizeof(RequestedSignalMask)};
185 thread->clearInterruption();
188 const int invalidNfdsResult =
189 posix_pselect6(-1,
nullptr,
nullptr,
nullptr, &invalidNfdsTimeout, &validSignalArgument);
190 const bool invalidNfdsMaskRestored = thread->
getSignalMask() == OriginalSignalMask;
191 passed &= invalidNfdsResult == -1 && thread->
getErrno() == Error::InvalidArgument &&
192 invalidNfdsMaskRestored && !invalidNfdsTimeout.tv_sec && !invalidNfdsTimeout.tv_nsec;
196 const int badFdsetResult = posix_pselect6(1,
reinterpret_cast<fd_set*
>(kernelStart),
nullptr,
197 nullptr, &badFdsetTimeout, &validSignalArgument);
198 const bool badFdsetMaskRestored = thread->
getSignalMask() == OriginalSignalMask;
199 passed &= badFdsetResult == -1 && thread->
getErrno() == Error::BadAddress &&
200 badFdsetMaskRestored && !badFdsetTimeout.tv_sec && !badFdsetTimeout.tv_nsec;
202 thread->clearInterruption();
204 const int eventFd = posix_eventfd(1);
205 uint64_t readyReads[2] = {};
206 uint64_t readyWrites[2] = {};
207 const int readyNfds = eventFd + 1;
208 if (eventFd >= 0 && readyNfds <=
static_cast<int>(BitsPerWord * 2)) {
209 setBit(readyReads, eventFd);
210 setBit(readyWrites, eventFd);
216 const int saturatedResult =
217 posix_pselect6(readyNfds,
reinterpret_cast<fd_set*
>(readyReads),
218 reinterpret_cast<fd_set*
>(readyWrites),
nullptr, &saturatedTimeout,
nullptr);
219 passed &= saturatedResult == 2 && isSet(readyReads, eventFd) && isSet(readyWrites, eventFd) &&
220 saturatedTimeout.tv_sec >= 0 && saturatedTimeout.tv_nsec >= 0 &&
221 saturatedTimeout.tv_nsec < 1000000000 && thread->
getErrno() == PreservedErrno;
223 uint64_t invalidDescriptor[1] = {};
224 setBit(invalidDescriptor, BitsPerWord - 1);
227 passed &= posix_pselect6(BitsPerWord,
reinterpret_cast<fd_set*
>(invalidDescriptor),
nullptr,
228 nullptr, &zero,
nullptr) == -1 &&
229 thread->
getErrno() == Error::BadFileDescriptor;
232 uintptr_t address = 0;
233 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
234 uintptr_t mappedAddress = address;
237 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
239 bool dynamicBitmaps = mapping && mappedAddress == address;
240 if (dynamicBitmaps) {
241 uint64_t emptyWord = 0;
242 fd_set* oneWord =
reinterpret_cast<fd_set*
>(address + pageSize -
sizeof(emptyWord));
245 posix_pselect6(1, oneWord,
nullptr,
nullptr, &zero,
nullptr) == 0;
247 constexpr int HighNfds = 1058;
248 constexpr size_t HighWords = (HighNfds + BitsPerWord - 1) / BitsPerWord;
249 uint64_t highBits[HighWords] = {};
250 setBit(highBits, context->highReadFd);
251 const size_t highExtent = bitmapExtent(HighNfds);
252 fd_set* highSet =
reinterpret_cast<fd_set*
>(address + pageSize - highExtent);
254 dynamicBitmaps &= context->highReadFd <
static_cast<size_t>(HighNfds) &&
256 posix_pselect6(HighNfds, highSet,
nullptr,
nullptr, &zero,
nullptr) == 1 &&
258 isSet(highBits, context->highReadFd);
261 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
262 }
else if (allocated) {
266 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
268 passed &= dynamicBitmaps;
269 passed &= eventFd >= 0 && posix_close(eventFd) == 0;
271 context->passed = passed;
272 context->returned += 1;
273 return passed ? 0 : 1;
276bool pselectValidationAndDynamicBitmaps(
Process* kernelProcess) {
277 constexpr size_t HighReadDescriptor = 1057;
278 constexpr size_t HighWriteDescriptor = 1058;
281 process->setSubsystem(subsystem);
289 const bool madeReady = writer->
write(1,
reinterpret_cast<uintptr_t
>(&value),
true) == 1;
290 PselectValidationContext context(HighReadDescriptor);
292 new Thread(process, pselectValidationWorker, &context,
nullptr,
false,
true,
true);
293 worker->setName(
"hosted pselect validation worker");
294 const bool started = madeReady &&
worker->start();
295 const bool joined = started &&
worker->joinForCompletion();
300 const bool writerClosed = closeDescriptor(subsystem, HighWriteDescriptor);
301 const bool readerClosed = closeDescriptor(subsystem, HighReadDescriptor);
303 started && joined && context.returned == 1 && context.passed && writerClosed && readerClosed;
307 "HOSTED-SYSCALL-TEST: FAIL pselect-validation-bitmap: "
308 "Linux ABI validation, ready-bit counting, or bitmap extent regressed");
311 NOTICE(
"HOSTED-SYSCALL-TEST: PASS pselect-validation-bitmap");
315struct PselectSignalContext {
316 explicit PselectSignalContext(
size_t readFd)
332 uint64_t restoredMask;
336int pselectSignalWorker(
void* parameter) {
337 PselectSignalContext* context =
reinterpret_cast<PselectSignalContext*
>(parameter);
339 uint64_t readBits[2] = {};
340 setBit(readBits, context->readFd);
342 sizeof(RequestedSignalMask)};
344 thread->clearInterruption();
345 context->entered += 1;
348 posix_pselect6(
static_cast<int>(context->readFd + 1),
reinterpret_cast<fd_set*
>(readBits),
349 nullptr,
nullptr, &context->timeout, &argument);
350 context->error = thread->
getErrno();
351 context->readReady = isSet(readBits, context->readFd);
354 thread->clearInterruption();
355 context->returned += 1;
359bool pselectSignalRace(
Process* kernelProcess,
bool readyWins) {
360 constexpr size_t ReadDescriptor = 91;
361 constexpr size_t WriteDescriptor = 92;
364 process->setSubsystem(subsystem);
371 g_PselectSignalHandlerCalls = 0;
372 g_PselectSignalHandlerWrites = 0;
373 g_PselectSignalWriter = readyWins ? writer :
nullptr;
374 PselectSignalContext context(ReadDescriptor);
375 Thread*
worker =
new Thread(process, pselectSignalWorker, &context,
nullptr,
false,
true,
true);
377 worker->setName(
"hosted pselect ready-signal worker");
379 worker->setName(
"hosted pselect EINTR worker");
381 const bool started =
worker->start();
382 while (started && !context.entered) {
385 const bool blocked = started && waitForPselectBlock(
worker);
386 const bool activeMaskObserved = blocked &&
worker->getSignalMask() == ActiveSignalMask;
388 TestSignal, ~0UL, 0,
true,
true);
389 const bool signalQueued = blocked &&
worker->sendEvent(signal);
394 bool rescueWrite =
false;
395 const Time::Timestamp returnDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
396 while (started && !context.returned && Time::getTicks() < returnDeadline) {
399 if (started && !context.returned) {
401 rescueWrite = writer->
write(1,
reinterpret_cast<uintptr_t
>(&rescue),
true) == 1;
403 const bool joined = started &&
worker->joinForCompletion();
407 g_PselectSignalWriter =
nullptr;
409 const bool resultPassed =
410 readyWins ? context.result == 1 && context.readReady && g_PselectSignalHandlerWrites == 1
411 : context.result == -1 && context.error == Error::Interrupted &&
412 !g_PselectSignalHandlerWrites;
413 bool passed = started && blocked && activeMaskObserved && signalQueued && !rescueWrite &&
414 joined && context.returned == 1 && resultPassed &&
415 context.restoredMask == OriginalSignalMask && g_PselectSignalHandlerCalls == 1 &&
416 context.timeout.tv_sec >= 0 && context.timeout.tv_sec <= 5 &&
417 context.timeout.tv_nsec >= 0 && context.timeout.tv_nsec < 1000000000;
418 const bool writerClosed = closeDescriptor(subsystem, WriteDescriptor);
419 const bool readerClosed = closeDescriptor(subsystem, ReadDescriptor);
420 passed &= writerClosed && readerClosed;
423 ERROR(
"HOSTED-SYSCALL-TEST: FAIL "
424 << (readyWins ?
"pselect-ready-beats-signal: " :
"pselect-eintr-mask: ")
425 <<
"temporary mask, restoration, or final readiness precedence regressed");
428 NOTICE(
"HOSTED-SYSCALL-TEST: PASS "
429 << (readyWins ?
"pselect-ready-beats-signal" :
"pselect-eintr-mask"));
433struct PselectOutputFaultContext {
434 explicit PselectOutputFaultContext(
size_t readFd)
435 : readFd(readFd), entered(0), returned(0), result(-2), error(0), passed(false) {}
445int pselectOutputFaultWorker(
void* parameter) {
446 PselectOutputFaultContext* context =
reinterpret_cast<PselectOutputFaultContext*
>(parameter);
450 const int nfds =
static_cast<int>(context->readFd + 1);
451 const size_t extent = bitmapExtent(nfds);
452 uintptr_t address = 0;
453 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
454 uintptr_t mappedAddress = address;
457 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
459 fd_set* readSet =
reinterpret_cast<fd_set*
>(address + pageSize - extent);
460 uint64_t readBits[2] = {};
461 uint64_t writeBits[2] = {};
462 setBit(readBits, context->readFd);
463 setBit(writeBits, context->readFd);
464 const bool prepared =
465 mapping && mappedAddress == address &&
471 sizeof(RequestedSignalMask)};
473 thread->clearInterruption();
475 context->entered += 1;
477 context->result = posix_pselect6(nfds, readSet,
reinterpret_cast<fd_set*
>(writeBits),
nullptr,
478 &timeout, &argument);
479 context->error = thread->
getErrno();
481 const bool maskRestored = thread->
getSignalMask() == OriginalSignalMask;
483 thread->clearInterruption();
484 const bool timeoutWritten = timeout.tv_sec >= 0 && timeout.tv_sec < 5 && timeout.tv_nsec >= 0 &&
485 timeout.tv_nsec < 1000000000;
486 const bool laterOutputUntouched = isSet(writeBits, context->readFd);
487 context->passed = prepared && context->result == -1 && context->error == Error::BadAddress &&
488 maskRestored && timeoutWritten && laterOutputUntouched;
494 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
496 context->returned += 1;
497 return context->passed ? 0 : 1;
500bool pselectOutputFaultCleanup(
Process* kernelProcess) {
501 constexpr size_t ReadDescriptor = 93;
502 constexpr size_t WriteDescriptor = 94;
505 process->setSubsystem(subsystem);
512 PselectOutputFaultContext context(ReadDescriptor);
514 new Thread(process, pselectOutputFaultWorker, &context,
nullptr,
false,
true,
true);
515 worker->setName(
"hosted pselect output fault worker");
516 const bool started =
worker->start();
517 while (started && !context.entered && !context.returned) {
520 const bool blocked = started && waitForPselectBlock(
worker);
521 const bool activeMaskObserved = blocked &&
worker->getSignalMask() == ActiveSignalMask;
523 const bool madeReady =
524 blocked && writer->
write(1,
reinterpret_cast<uintptr_t
>(&value),
true) == 1;
525 const bool joined = started &&
worker->joinForCompletion();
529 bool passed = started && blocked && activeMaskObserved && madeReady && joined &&
530 context.returned == 1 && context.passed;
531 const bool writerClosed = closeDescriptor(subsystem, WriteDescriptor);
532 const bool readerClosed = closeDescriptor(subsystem, ReadDescriptor);
533 passed &= writerClosed && readerClosed;
537 "HOSTED-SYSCALL-TEST: FAIL pselect-output-fault: "
538 "mask cleanup, timeout writeback, or fdset copyout order regressed");
541 NOTICE(
"HOSTED-SYSCALL-TEST: PASS pselect-output-fault");
546bool runHostedPselectRegressions(
Process* process) {
547 return pselectValidationAndDynamicBitmaps(process) && pselectSignalRace(process,
false) &&
548 pselectSignalRace(process,
true) && pselectOutputFaultCleanup(process);
Memory-mapped file interface.
uint64_t write(uint64_t size, uintptr_t buffer, bool canBlock=true)
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
bool closeFileDescriptor(size_t fd, const DescriptorLease &descriptor)
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
static ProcessorInformation & information()
static Scheduler & instance()
void setErrno(size_t err)
bool getWaitDebugInfo(WaitDebugInfo &info)
DebugState getDebugState(uintptr_t &address)
Process * getParent() const
void setSignalMask(uint64_t mask)