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/Processor.h"
16#include "pedigree/kernel/processor/VirtualAddressSpace.h"
17#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/linux-wait-abi.h"
28#include "modules/subsys/posix/poll-syscalls.h"
29#include "modules/system/vfs/Pipe.h"
36constexpr size_t TestSignal = 10;
37constexpr int PreservedErrno = 123;
38constexpr uint64_t TestSignalBit =
static_cast<uint64_t
>(1) << (TestSignal - 1);
39constexpr uint64_t PreservedSignalBit =
static_cast<uint64_t
>(1) << (12 - 1);
40constexpr uint64_t UnblockableSignalBits =
41 (
static_cast<uint64_t
>(1) << (SIGKILL - 1)) | (
static_cast<uint64_t
>(1) << (SIGSTOP - 1));
42constexpr uint64_t OriginalSignalMask = TestSignalBit | PreservedSignalBit;
43constexpr uint64_t RequestedSignalMask = PreservedSignalBit | UnblockableSignalBits;
44constexpr uint64_t ActiveSignalMask = PreservedSignalBit;
50void ppollSignalHandler(
size_t) {
51 g_PpollSignalHandlerCalls += 1;
52 if (g_PpollSignalWriter) {
54 if (g_PpollSignalWriter->
write(1,
reinterpret_cast<uintptr_t
>(&value),
true) == 1) {
55 g_PpollSignalHandlerWrites += 1;
70bool waitForPpollBlock(
Thread* thread) {
71 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
72 while (Time::getTicks() < deadline) {
74 uintptr_t debugAddress = 0;
84struct PpollValidationContext {
85 explicit PpollValidationContext(
size_t readFd) : readFd(readFd), passed(false), returned(0) {}
92int ppollValidationWorker(
void* parameter) {
93 PpollValidationContext* context =
reinterpret_cast<PpollValidationContext*
>(parameter);
99 const int zeroResult = posix_ppoll(
nullptr, 0, &zero,
nullptr, 37);
100 passed = passed && zeroResult == 0 && thread->
getErrno() == PreservedErrno;
104 const int oneNanosecondResult = posix_ppoll(
nullptr, 0, &oneNanosecond,
nullptr, 0);
105 passed = passed && oneNanosecondResult == 0 && !oneNanosecond.tv_sec && !oneNanosecond.tv_nsec &&
106 thread->
getErrno() == PreservedErrno;
110 const int negativeSecondsResult = posix_ppoll(
nullptr, 0, &negativeSeconds,
nullptr, 0);
111 passed = passed && negativeSecondsResult == -1 && thread->
getErrno() == Error::InvalidArgument;
115 const int negativeNanosecondsResult = posix_ppoll(
nullptr, 0, &negativeNanoseconds,
nullptr, 0);
117 passed && negativeNanosecondsResult == -1 && thread->
getErrno() == Error::InvalidArgument;
121 const int excessiveNanosecondsResult = posix_ppoll(
nullptr, 0, &excessiveNanoseconds,
nullptr, 0);
123 passed && excessiveNanosecondsResult == -1 && thread->
getErrno() == Error::InvalidArgument;
125 uint64_t signalMask = 0;
128 const int wrongMaskSizeResult =
129 posix_ppoll(
nullptr, 0, &zero, &signalMask,
sizeof(signalMask) - 1);
130 passed = passed && wrongMaskSizeResult == -1 && thread->
getErrno() == Error::InvalidArgument;
134 const int badTimeoutResult =
136 passed = passed && badTimeoutResult == -1 && thread->
getErrno() == Error::BadAddress;
140 const int excessiveDescriptorsResult = posix_ppoll(
nullptr, 16385, &zero,
nullptr, 0);
142 passed && excessiveDescriptorsResult == -1 && thread->
getErrno() == Error::InvalidArgument;
144 struct pollfd ready = {
static_cast<int>(context->readFd), POLLIN, 0};
146 const int readyResult = posix_ppoll(&ready, 1,
nullptr,
nullptr, 91);
147 passed = passed && readyResult == 1 && (ready.revents & POLLIN) &&
148 thread->
getErrno() == PreservedErrno;
151 struct pollfd saturatedReady = {
static_cast<int>(context->readFd), POLLIN, 0};
153 const int saturatedResult = posix_ppoll(&saturatedReady, 1, &saturatedTimeout,
nullptr, 0);
154 passed = passed && saturatedResult == 1 && (saturatedReady.revents & POLLIN) &&
155 saturatedTimeout.tv_sec >= 0 && saturatedTimeout.tv_nsec >= 0 &&
156 saturatedTimeout.tv_nsec < 1000000000 && thread->
getErrno() == PreservedErrno;
159 const uint64_t blockedMask = previousMask | TestSignalBit;
160 const uint64_t temporaryMask = blockedMask & ~TestSignalBit;
162 thread->clearInterruption();
166 const int badInputResult = posix_ppoll(
reinterpret_cast<struct pollfd*
>(kernelStart), 1,
167 &badInputTimeout, &temporaryMask,
sizeof(temporaryMask));
168 const int badInputError = thread->
getErrno();
169 const bool badInputMaskRestored = thread->
getSignalMask() == blockedMask;
170 const bool validErrorRemainder = badInputTimeout.tv_sec >= 0 && badInputTimeout.tv_sec <= 10 &&
171 badInputTimeout.tv_nsec >= 0 &&
172 badInputTimeout.tv_nsec < 1000000000 &&
173 (badInputTimeout.tv_sec < 10 || !badInputTimeout.tv_nsec);
175 g_PpollSignalHandlerCalls = 0;
176 g_PpollSignalWriter =
nullptr;
177 SignalEvent pendingSignal(
reinterpret_cast<uintptr_t
>(&ppollSignalHandler), TestSignal);
178 const bool signalQueued = thread->
sendEvent(&pendingSignal);
182 const int badMaskBeforeArmResult = posix_ppoll(
183 nullptr, 0, &zero,
reinterpret_cast<const uint64_t*
>(kernelStart),
sizeof(signalMask));
184 const bool maskRestoredBeforeReturn = thread->
getSignalMask() == blockedMask;
185 const bool signalStillPending = thread->
hasEvent(&pendingSignal);
186 const bool signalStayedBlocked = !g_PpollSignalHandlerCalls;
187 if (signalStillPending) {
191 thread->clearInterruption();
193 passed = passed && signalQueued && badInputResult == -1 && badInputError == Error::BadAddress &&
194 badInputMaskRestored && validErrorRemainder && badMaskBeforeArmResult == -1 &&
195 thread->
getErrno() == Error::BadAddress && maskRestoredBeforeReturn &&
196 signalStillPending && signalStayedBlocked;
197 context->passed = passed;
198 context->returned += 1;
199 return passed ? 0 : 1;
202bool ppollValidationAndImmediateReadiness(
Process* kernelProcess) {
203 constexpr size_t ReadDescriptor = 89;
204 constexpr size_t WriteDescriptor = 90;
208 process->setSubsystem(subsystem);
216 const bool madeReady = writer->
write(1,
reinterpret_cast<uintptr_t
>(&value),
true) == 1;
217 PpollValidationContext context(ReadDescriptor);
218 Thread*
worker =
new Thread(process, ppollValidationWorker, &context,
nullptr,
false,
true,
true);
219 worker->setName(
"hosted ppoll validation worker");
220 const bool started = madeReady &&
worker->start();
221 const bool joined = started &&
worker->joinForCompletion();
226 const bool writerClosed = closeDescriptor(subsystem, WriteDescriptor);
227 const bool readerClosed = closeDescriptor(subsystem, ReadDescriptor);
229 started && joined && context.returned == 1 && context.passed && writerClosed && readerClosed;
234 "HOSTED-SYSCALL-TEST: FAIL ppoll-validation-immediate: "
235 "Linux timeout, sigset, input-copy, or immediate-readiness semantics regressed");
239 NOTICE(
"HOSTED-SYSCALL-TEST: PASS ppoll-validation-immediate");
243struct PpollSignalContext {
244 explicit PpollSignalContext(
size_t readFd)
245 : readFd(readFd), entered(0), returned(0), result(-2), error(0), events(0), restoredMask(0) {}
253 uint64_t restoredMask;
256int ppollSignalWorker(
void* parameter) {
257 PpollSignalContext* context =
reinterpret_cast<PpollSignalContext*
>(parameter);
260 thread->clearInterruption();
262 struct pollfd descriptor = {
static_cast<int>(context->readFd), POLLIN, 0};
263 context->entered += 1;
266 posix_ppoll(&descriptor, 1,
nullptr, &RequestedSignalMask,
sizeof(RequestedSignalMask));
267 context->error = thread->
getErrno();
268 context->events = descriptor.revents;
271 thread->clearInterruption();
272 context->returned += 1;
276bool ppollSignalRace(
Process* kernelProcess,
bool readyWins) {
277 constexpr size_t ReadDescriptor = 91;
278 constexpr size_t WriteDescriptor = 92;
282 process->setSubsystem(subsystem);
289 g_PpollSignalHandlerCalls = 0;
290 g_PpollSignalHandlerWrites = 0;
291 g_PpollSignalWriter = readyWins ? writer :
nullptr;
292 PpollSignalContext context(ReadDescriptor);
293 Thread*
worker =
new Thread(process, ppollSignalWorker, &context,
nullptr,
false,
true,
true);
295 worker->setName(
"hosted ppoll ready-signal worker");
297 worker->setName(
"hosted ppoll EINTR worker");
299 const bool started =
worker->start();
300 while (started && !context.entered) {
303 const bool blocked = started && waitForPpollBlock(
worker);
304 const bool activeMaskObserved = blocked &&
worker->getSignalMask() == ActiveSignalMask;
307 TestSignal, ~0UL, 0,
true,
true);
308 const bool signalQueued = blocked &&
worker->sendEvent(signal);
313 bool rescueWrite =
false;
314 if (!blocked && started && !context.returned) {
316 rescueWrite = writer->
write(1,
reinterpret_cast<uintptr_t
>(&rescue),
true) == 1;
318 const Time::Timestamp returnDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
319 while (started && !context.returned && Time::getTicks() < returnDeadline) {
322 if (started && !context.returned) {
324 rescueWrite = writer->
write(1,
reinterpret_cast<uintptr_t
>(&rescue),
true) == 1 || rescueWrite;
326 const bool joined = started &&
worker->joinForCompletion();
330 g_PpollSignalWriter =
nullptr;
332 const bool resultPassed =
334 ? context.result == 1 && (context.events & POLLIN) && g_PpollSignalHandlerWrites == 1
335 : context.result == -1 && context.error == Error::Interrupted && !context.events &&
336 !g_PpollSignalHandlerWrites;
337 bool passed = started && blocked && activeMaskObserved && signalQueued && !rescueWrite &&
338 joined && context.returned == 1 && resultPassed &&
339 context.restoredMask == OriginalSignalMask && g_PpollSignalHandlerCalls == 1;
341 const bool writerClosed = closeDescriptor(subsystem, WriteDescriptor);
342 const bool readerClosed = closeDescriptor(subsystem, ReadDescriptor);
343 passed = passed && writerClosed && readerClosed;
347 ERROR(
"HOSTED-SYSCALL-TEST: FAIL "
348 << (readyWins ?
"ppoll-ready-beats-signal: " :
"ppoll-eintr-mask: ")
349 <<
"temporary masking, restoration, or ready-vs-signal precedence regressed");
353 NOTICE(
"HOSTED-SYSCALL-TEST: PASS "
354 << (readyWins ?
"ppoll-ready-beats-signal" :
"ppoll-eintr-mask"));
359bool runHostedPpollRegressions(
Process* process) {
360 return ppollValidationAndImmediateReadiness(process) && ppollSignalRace(process,
false) &&
361 ppollSignalRace(process,
true);
uint64_t write(uint64_t size, uintptr_t buffer, bool canBlock=true)
bool acquireFileDescriptor(size_t fd, DescriptorLease &descriptor)
bool closeFileDescriptor(size_t fd, const DescriptorLease &descriptor)
void addFileDescriptor(size_t fd, FileDescriptor *pFd)
static ProcessorInformation & information()
static Scheduler & instance()
void setErrno(size_t err)
bool getWaitDebugInfo(WaitDebugInfo &info)
bool hasEvent(Event *pEvent)
void cullEvent(Event *pEvent)
DebugState getDebugState(uintptr_t &address)
void setSignalMask(uint64_t mask)
bool sendEvent(Event *pEvent)