The Pedigree Project 0.1
pselect-regressions.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
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"
19
20#include <fcntl.h>
21#include <signal.h>
22#include <stddef.h>
23#include <stdint.h>
24
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"
32
33static_assert(sizeof(LinuxPselectSigsetArgument) == 16);
34static_assert(offsetof(LinuxPselectSigsetArgument, signalMask) == 0);
35static_assert(offsetof(LinuxPselectSigsetArgument, signalMaskSize) == 8);
36
37namespace {
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;
48
49Atomic<size_t> g_PselectSignalHandlerCalls(0);
50Atomic<size_t> g_PselectSignalHandlerWrites(0);
51FileDescriptor* g_PselectSignalWriter = nullptr;
52
53size_t bitmapExtent(int nfds) {
54 return ((static_cast<size_t>(nfds) + BitsPerWord - 1) / BitsPerWord) * sizeof(uint64_t);
55}
56
57void setBit(uint64_t* words, size_t fd) {
58 words[fd / BitsPerWord] |= static_cast<uint64_t>(1) << (fd % BitsPerWord);
59}
60
61bool isSet(const uint64_t* words, size_t fd) {
62 return words[fd / BitsPerWord] & (static_cast<uint64_t>(1) << (fd % BitsPerWord));
63}
64
65void pselectSignalHandler(size_t) {
66 g_PselectSignalHandlerCalls += 1;
67 if (g_PselectSignalWriter) {
68 char value = 'r';
69 if (g_PselectSignalWriter->write(1, reinterpret_cast<uintptr_t>(&value), true) == 1) {
70 g_PselectSignalHandlerWrites += 1;
71 }
72 }
73}
74
75bool closeDescriptor(PosixSubsystem* subsystem, size_t fd) {
76 DescriptorLease descriptor;
77 if (!subsystem->acquireFileDescriptor(fd, descriptor)) {
78 return false;
79 }
80 const bool closed = subsystem->closeFileDescriptor(fd, descriptor);
81 descriptor.reset();
82 return closed;
83}
84
85bool waitForPselectBlock(Thread* thread) {
86 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
87 while (Time::getTicks() < deadline) {
88 Thread::WaitDebugInfo info = {};
89 uintptr_t debugAddress = 0;
90 if (thread->getWaitDebugInfo(info) && info.queue && info.queued &&
91 thread->getDebugState(debugAddress) == Thread::SemWait) {
92 return true;
93 }
95 }
96 return false;
97}
98
99struct PselectValidationContext {
100 explicit PselectValidationContext(size_t highReadFd)
101 : highReadFd(highReadFd), passed(false), returned(0) {}
102
103 size_t highReadFd;
104 bool passed;
105 Atomic<size_t> returned;
106};
107
108int pselectValidationWorker(void* parameter) {
109 PselectValidationContext* context = reinterpret_cast<PselectValidationContext*>(parameter);
110 Thread* thread = Processor::information().getCurrentThread();
111 Process* process = thread->getParent();
112 bool passed = true;
113
114 LinuxKernelTimespec zero = {0, 0};
115 thread->setErrno(PreservedErrno);
116 const int zeroResult = posix_pselect6(0, nullptr, nullptr, nullptr, &zero, nullptr);
117 passed &= zeroResult == 0 && thread->getErrno() == PreservedErrno;
118
119 LinuxKernelTimespec oneNanosecond = {0, 1};
120 thread->setErrno(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;
125
126 LinuxKernelTimespec negativeSeconds = {-1, 0};
127 thread->setErrno(0);
128 passed &= posix_pselect6(0, nullptr, nullptr, nullptr, &negativeSeconds, nullptr) == -1 &&
129 thread->getErrno() == Error::InvalidArgument;
130
131 LinuxKernelTimespec negativeNanoseconds = {0, -1};
132 thread->setErrno(0);
133 passed &= posix_pselect6(0, nullptr, nullptr, nullptr, &negativeNanoseconds, nullptr) == -1 &&
134 thread->getErrno() == Error::InvalidArgument;
135
136 LinuxKernelTimespec excessiveNanoseconds = {0, 1000000000};
137 thread->setErrno(0);
138 passed &= posix_pselect6(0, nullptr, nullptr, nullptr, &excessiveNanoseconds, nullptr) == -1 &&
139 thread->getErrno() == Error::InvalidArgument;
140
141 uint64_t signalMask = 0;
142 LinuxKernelTimespec unchangedWrongSize = {1, 123};
143 LinuxPselectSigsetArgument wrongMaskSize = {reinterpret_cast<uintptr_t>(&signalMask),
144 sizeof(signalMask) - 1};
145 thread->setErrno(0);
146 passed &=
147 posix_pselect6(0, nullptr, nullptr, nullptr, &unchangedWrongSize, &wrongMaskSize) == -1 &&
148 thread->getErrno() == Error::InvalidArgument && unchangedWrongSize.tv_sec == 1 &&
149 unchangedWrongSize.tv_nsec == 123;
150
151 LinuxPselectSigsetArgument nullMask = {0, 1};
152 zero = {0, 0};
153 thread->setErrno(PreservedErrno);
154 passed &= posix_pselect6(0, nullptr, nullptr, nullptr, &zero, &nullMask) == 0 &&
155 thread->getErrno() == PreservedErrno;
156
157 const uintptr_t kernelStart = Processor::information().getVirtualAddressSpace().getKernelStart();
158 LinuxKernelTimespec invalidWithBadArgument = {-1, 0};
159 thread->setErrno(0);
160 passed &=
161 posix_pselect6(0, nullptr, nullptr, nullptr, &invalidWithBadArgument,
162 reinterpret_cast<const LinuxPselectSigsetArgument*>(kernelStart)) == -1 &&
163 thread->getErrno() == Error::BadAddress;
164
165 LinuxKernelTimespec unchangedBadMask = {1, 456};
166 LinuxPselectSigsetArgument badMask = {kernelStart, sizeof(signalMask)};
167 thread->setErrno(0);
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;
171
172 LinuxKernelTimespec invalidBeforeBadMask = {-1, 0};
173 thread->setErrno(0);
174 passed &= posix_pselect6(0, nullptr, nullptr, nullptr, &invalidBeforeBadMask, &badMask) == -1 &&
175 thread->getErrno() == Error::InvalidArgument;
176
177 thread->setErrno(0);
178 passed &= posix_pselect6(0, nullptr, nullptr, nullptr,
179 reinterpret_cast<LinuxKernelTimespec*>(kernelStart), nullptr) == -1 &&
180 thread->getErrno() == Error::BadAddress;
181
182 LinuxPselectSigsetArgument validSignalArgument = {
183 reinterpret_cast<uintptr_t>(&RequestedSignalMask), sizeof(RequestedSignalMask)};
184 thread->setSignalMask(OriginalSignalMask);
185 thread->clearInterruption();
186 LinuxKernelTimespec invalidNfdsTimeout = {0, 1};
187 thread->setErrno(0);
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;
193
194 LinuxKernelTimespec badFdsetTimeout = {0, 1};
195 thread->setErrno(0);
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;
201 thread->setSignalMask(0);
202 thread->clearInterruption();
203
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);
211 } else {
212 passed = false;
213 }
214 LinuxKernelTimespec saturatedTimeout = {INT64_MAX, 999999999};
215 thread->setErrno(PreservedErrno);
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;
222
223 uint64_t invalidDescriptor[1] = {};
224 setBit(invalidDescriptor, BitsPerWord - 1);
225 zero = {0, 0};
226 thread->setErrno(0);
227 passed &= posix_pselect6(BitsPerWord, reinterpret_cast<fd_set*>(invalidDescriptor), nullptr,
228 nullptr, &zero, nullptr) == -1 &&
229 thread->getErrno() == Error::BadFileDescriptor;
230
231 const size_t pageSize = PhysicalMemoryManager::getPageSize();
232 uintptr_t address = 0;
233 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
234 uintptr_t mappedAddress = address;
235 MemoryMappedObject* mapping =
237 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
238 : nullptr;
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));
243 zero = {0, 0};
244 dynamicBitmaps &= PosixSubsystem::copyToUser(oneWord, &emptyWord, sizeof(emptyWord)) &&
245 posix_pselect6(1, oneWord, nullptr, nullptr, &zero, nullptr) == 0;
246
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);
253 zero = {0, 0};
254 dynamicBitmaps &= context->highReadFd < static_cast<size_t>(HighNfds) &&
255 PosixSubsystem::copyToUser(highSet, highBits, highExtent) &&
256 posix_pselect6(HighNfds, highSet, nullptr, nullptr, &zero, nullptr) == 1 &&
257 PosixSubsystem::copyFromUser(highBits, highSet, highExtent) &&
258 isSet(highBits, context->highReadFd);
259
260 dynamicBitmaps &= MemoryMapManager::instance().remove(address, pageSize) == 1;
261 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
262 } else if (allocated) {
263 if (mapping) {
264 MemoryMapManager::instance().remove(mappedAddress, pageSize);
265 }
266 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
267 }
268 passed &= dynamicBitmaps;
269 passed &= eventFd >= 0 && posix_close(eventFd) == 0;
270
271 context->passed = passed;
272 context->returned += 1;
273 return passed ? 0 : 1;
274}
275
276bool pselectValidationAndDynamicBitmaps(Process* kernelProcess) {
277 constexpr size_t HighReadDescriptor = 1057;
278 constexpr size_t HighWriteDescriptor = 1058;
279 Process* process = new Process(kernelProcess);
280 PosixSubsystem* subsystem = new PosixSubsystem;
281 process->setSubsystem(subsystem);
282 Pipe* pipe = new Pipe;
283 FileDescriptor* reader = new FileDescriptor(pipe, 0, HighReadDescriptor, 0, O_RDONLY);
284 FileDescriptor* writer = new FileDescriptor(pipe, 0, HighWriteDescriptor, 0, O_WRONLY);
285 subsystem->addFileDescriptor(HighReadDescriptor, reader);
286 subsystem->addFileDescriptor(HighWriteDescriptor, writer);
287
288 char value = 'v';
289 const bool madeReady = writer->write(1, reinterpret_cast<uintptr_t>(&value), true) == 1;
290 PselectValidationContext context(HighReadDescriptor);
291 Thread* worker =
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();
296 if (!started) {
297 delete worker;
298 }
299
300 const bool writerClosed = closeDescriptor(subsystem, HighWriteDescriptor);
301 const bool readerClosed = closeDescriptor(subsystem, HighReadDescriptor);
302 const bool passed =
303 started && joined && context.returned == 1 && context.passed && writerClosed && readerClosed;
304 delete process;
305 if (!passed) {
306 ERROR(
307 "HOSTED-SYSCALL-TEST: FAIL pselect-validation-bitmap: "
308 "Linux ABI validation, ready-bit counting, or bitmap extent regressed");
309 return false;
310 }
311 NOTICE("HOSTED-SYSCALL-TEST: PASS pselect-validation-bitmap");
312 return true;
313}
314
315struct PselectSignalContext {
316 explicit PselectSignalContext(size_t readFd)
317 : readFd(readFd),
318 entered(0),
319 returned(0),
320 result(-2),
321 error(0),
322 readReady(false),
323 restoredMask(0),
324 timeout({5, 0}) {}
325
326 size_t readFd;
327 Atomic<size_t> entered;
328 Atomic<size_t> returned;
329 int result;
330 int error;
331 bool readReady;
332 uint64_t restoredMask;
333 LinuxKernelTimespec timeout;
334};
335
336int pselectSignalWorker(void* parameter) {
337 PselectSignalContext* context = reinterpret_cast<PselectSignalContext*>(parameter);
338 Thread* thread = Processor::information().getCurrentThread();
339 uint64_t readBits[2] = {};
340 setBit(readBits, context->readFd);
341 LinuxPselectSigsetArgument argument = {reinterpret_cast<uintptr_t>(&RequestedSignalMask),
342 sizeof(RequestedSignalMask)};
343 thread->setSignalMask(OriginalSignalMask);
344 thread->clearInterruption();
345 context->entered += 1;
346 thread->setErrno(PreservedErrno);
347 context->result =
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);
352 context->restoredMask = thread->getSignalMask();
353 thread->setSignalMask(0);
354 thread->clearInterruption();
355 context->returned += 1;
356 return 0;
357}
358
359bool pselectSignalRace(Process* kernelProcess, bool readyWins) {
360 constexpr size_t ReadDescriptor = 91;
361 constexpr size_t WriteDescriptor = 92;
362 Process* process = new Process(kernelProcess);
363 PosixSubsystem* subsystem = new PosixSubsystem;
364 process->setSubsystem(subsystem);
365 Pipe* pipe = new Pipe;
366 FileDescriptor* reader = new FileDescriptor(pipe, 0, ReadDescriptor, 0, O_RDONLY);
367 FileDescriptor* writer = new FileDescriptor(pipe, 0, WriteDescriptor, 0, O_WRONLY);
368 subsystem->addFileDescriptor(ReadDescriptor, reader);
369 subsystem->addFileDescriptor(WriteDescriptor, writer);
370
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);
376 if (readyWins) {
377 worker->setName("hosted pselect ready-signal worker");
378 } else {
379 worker->setName("hosted pselect EINTR worker");
380 }
381 const bool started = worker->start();
382 while (started && !context.entered) {
384 }
385 const bool blocked = started && waitForPselectBlock(worker);
386 const bool activeMaskObserved = blocked && worker->getSignalMask() == ActiveSignalMask;
387 SignalEvent* signal = new SignalEvent(reinterpret_cast<uintptr_t>(&pselectSignalHandler),
388 TestSignal, ~0UL, 0, true, true);
389 const bool signalQueued = blocked && worker->sendEvent(signal);
390 if (!signalQueued) {
391 delete signal;
392 }
393
394 bool rescueWrite = false;
395 const Time::Timestamp returnDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
396 while (started && !context.returned && Time::getTicks() < returnDeadline) {
398 }
399 if (started && !context.returned) {
400 char rescue = 'x';
401 rescueWrite = writer->write(1, reinterpret_cast<uintptr_t>(&rescue), true) == 1;
402 }
403 const bool joined = started && worker->joinForCompletion();
404 if (!started) {
405 delete worker;
406 }
407 g_PselectSignalWriter = nullptr;
408
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;
421 delete process;
422 if (!passed) {
423 ERROR("HOSTED-SYSCALL-TEST: FAIL "
424 << (readyWins ? "pselect-ready-beats-signal: " : "pselect-eintr-mask: ")
425 << "temporary mask, restoration, or final readiness precedence regressed");
426 return false;
427 }
428 NOTICE("HOSTED-SYSCALL-TEST: PASS "
429 << (readyWins ? "pselect-ready-beats-signal" : "pselect-eintr-mask"));
430 return true;
431}
432
433struct PselectOutputFaultContext {
434 explicit PselectOutputFaultContext(size_t readFd)
435 : readFd(readFd), entered(0), returned(0), result(-2), error(0), passed(false) {}
436
437 size_t readFd;
438 Atomic<size_t> entered;
439 Atomic<size_t> returned;
440 int result;
441 int error;
442 bool passed;
443};
444
445int pselectOutputFaultWorker(void* parameter) {
446 PselectOutputFaultContext* context = reinterpret_cast<PselectOutputFaultContext*>(parameter);
447 Thread* thread = Processor::information().getCurrentThread();
448 Process* process = thread->getParent();
449 const size_t pageSize = PhysicalMemoryManager::getPageSize();
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;
455 MemoryMappedObject* mapping =
457 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
458 : nullptr;
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 &&
466 PosixSubsystem::copyToUser(readSet, readBits, extent) &&
467 MemoryMapManager::instance().setPermissions(address, pageSize, MemoryMappedObject::Read) == 1;
468
469 LinuxKernelTimespec timeout = {5, 0};
470 LinuxPselectSigsetArgument argument = {reinterpret_cast<uintptr_t>(&RequestedSignalMask),
471 sizeof(RequestedSignalMask)};
472 thread->setSignalMask(OriginalSignalMask);
473 thread->clearInterruption();
474 if (prepared) {
475 context->entered += 1;
476 thread->setErrno(0);
477 context->result = posix_pselect6(nfds, readSet, reinterpret_cast<fd_set*>(writeBits), nullptr,
478 &timeout, &argument);
479 context->error = thread->getErrno();
480 }
481 const bool maskRestored = thread->getSignalMask() == OriginalSignalMask;
482 thread->setSignalMask(0);
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;
489
490 if (mapping) {
491 MemoryMapManager::instance().remove(address, pageSize);
492 }
493 if (allocated) {
494 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
495 }
496 context->returned += 1;
497 return context->passed ? 0 : 1;
498}
499
500bool pselectOutputFaultCleanup(Process* kernelProcess) {
501 constexpr size_t ReadDescriptor = 93;
502 constexpr size_t WriteDescriptor = 94;
503 Process* process = new Process(kernelProcess);
504 PosixSubsystem* subsystem = new PosixSubsystem;
505 process->setSubsystem(subsystem);
506 Pipe* pipe = new Pipe;
507 FileDescriptor* reader = new FileDescriptor(pipe, 0, ReadDescriptor, 0, O_RDONLY);
508 FileDescriptor* writer = new FileDescriptor(pipe, 0, WriteDescriptor, 0, O_WRONLY);
509 subsystem->addFileDescriptor(ReadDescriptor, reader);
510 subsystem->addFileDescriptor(WriteDescriptor, writer);
511
512 PselectOutputFaultContext context(ReadDescriptor);
513 Thread* worker =
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) {
519 }
520 const bool blocked = started && waitForPselectBlock(worker);
521 const bool activeMaskObserved = blocked && worker->getSignalMask() == ActiveSignalMask;
522 char value = 'o';
523 const bool madeReady =
524 blocked && writer->write(1, reinterpret_cast<uintptr_t>(&value), true) == 1;
525 const bool joined = started && worker->joinForCompletion();
526 if (!started) {
527 delete worker;
528 }
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;
534 delete process;
535 if (!passed) {
536 ERROR(
537 "HOSTED-SYSCALL-TEST: FAIL pselect-output-fault: "
538 "mask cleanup, timeout writeback, or fdset copyout order regressed");
539 return false;
540 }
541 NOTICE("HOSTED-SYSCALL-TEST: PASS pselect-output-fault");
542 return true;
543}
544} // namespace
545
546bool runHostedPselectRegressions(Process* process) {
547 return pselectValidationAndDynamicBitmaps(process) && pselectSignalRace(process, false) &&
548 pselectSignalRace(process, true) && pselectOutputFaultCleanup(process);
549}
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()
Definition Pipe.h:36
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()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:226
void setErrno(size_t err)
Definition Thread.h:478
uint64_t getSignalMask()
Definition Thread.cc:2002
bool getWaitDebugInfo(WaitDebugInfo &info)
Definition Thread.cc:3184
size_t getErrno()
Definition Thread.h:473
DebugState getDebugState(uintptr_t &address)
Definition Thread.h:570
Process * getParent() const
Definition Thread.h:338
void setSignalMask(uint64_t mask)
Definition Thread.cc:2007
Definition waits.c:9