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"
24#include "modules/subsys/posix/PosixProcess.h"
25#include "modules/subsys/posix/PosixSubsystem.h"
26#include "modules/subsys/posix/linux-wait-abi.h"
27#include "modules/subsys/posix/system-syscalls.h"
30extern "C" Time::Timestamp posixNanosleepAlarmDurationForTest(time_t seconds,
long nanoseconds);
32static_assert(
sizeof(time_t) == 8);
33static_assert(
static_cast<time_t
>(-1) < 0);
34static_assert(
sizeof(
struct timespec) == 16);
35static_assert(offsetof(
struct timespec, tv_sec) == 0);
36static_assert(offsetof(
struct timespec, tv_nsec) == 8);
39constexpr int PreservedErrno = 123;
40constexpr int IgnoredClockNanosleepFlag = 0x40000000;
41constexpr clockid_t UnsupportedCoarseClock = 6;
42constexpr time_t UntouchedRemainderSeconds = 123;
43constexpr long UntouchedRemainderNanoseconds = 456;
44constexpr size_t SleepSignal = 10;
45constexpr Time::Timestamp MaximumLinuxSleepNanoseconds = 0x7FFFFFFFFFFFFFFFULL;
46constexpr time_t MaximumLinuxSleepSeconds =
47 static_cast<time_t
>(MaximumLinuxSleepNanoseconds / Time::Multiplier::Second);
48constexpr Time::Timestamp RoundedMaximumLinuxSleepNanoseconds =
49 MaximumLinuxSleepNanoseconds + (Time::Multiplier::Microsecond -
50 (MaximumLinuxSleepNanoseconds % Time::Multiplier::Microsecond));
54void sleepSignalHandler(
size_t) {
55 g_SleepSignalCalls += 1;
58bool canonicalTimespec(
const struct timespec& value) {
59 return value.tv_sec >= 0 && value.tv_nsec >= 0 && value.tv_nsec < 1000000000;
62struct ValidationContext {
63 ValidationContext() : passed(false), returned(0) {}
69int exerciseSleepClockValidation(
void* parameter) {
70 ValidationContext* context =
reinterpret_cast<ValidationContext*
>(parameter);
73 bool passed = process->getType() == Process::Posix;
76 const size_t mappingLength = pageSize * 3;
77 uintptr_t address = 0;
78 const bool allocated =
79 process->allocateUserRange(Process::UserRegion::Normal, mappingLength, address);
80 uintptr_t mappedAddress = address;
84 mappedAddress, mappingLength, MemoryMappedObject::Read | MemoryMappedObject::Write)
86 if (!mapping || mappedAddress != address) {
91 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
93 context->returned += 1;
97 struct timespec* input =
reinterpret_cast<struct timespec*
>(address + 64);
98 struct timespec* output =
reinterpret_cast<struct timespec*
>(address + 256);
99 struct timespec* pageEdge =
100 reinterpret_cast<struct timespec*
>(address + pageSize - (
sizeof(
struct timespec) / 2));
101 struct timespec* readOnly =
reinterpret_cast<struct timespec*
>(address + (pageSize * 2) + 64);
103 struct timespec* bad =
reinterpret_cast<struct timespec*
>(kernelStart);
110 passed &= posixNanosleepAlarmDurationForTest(0, 1) == Time::Multiplier::Microsecond &&
111 posixNanosleepAlarmDurationForTest(0, 1000) == Time::Multiplier::Microsecond &&
112 posixNanosleepAlarmDurationForTest(0, 1001) == (2 * Time::Multiplier::Microsecond) &&
113 posixNanosleepAlarmDurationForTest(MaximumLinuxSleepSeconds, 0) ==
114 static_cast<Time::Timestamp
>(MaximumLinuxSleepSeconds) * Time::Multiplier::Second &&
115 posixNanosleepAlarmDurationForTest(MaximumLinuxSleepSeconds, 854775807) ==
116 RoundedMaximumLinuxSleepNanoseconds &&
117 posixNanosleepAlarmDurationForTest(MaximumLinuxSleepSeconds, 854775808) ==
118 RoundedMaximumLinuxSleepNanoseconds &&
119 posixNanosleepAlarmDurationForTest(MaximumLinuxSleepSeconds + 1, 0) ==
120 RoundedMaximumLinuxSleepNanoseconds;
122 struct timespec observed = {};
124 passed &= posix_clock_gettime(CLOCK_REALTIME, output) == 0 &&
125 thread->
getErrno() == PreservedErrno &&
127 canonicalTimespec(observed);
129 struct timespec monotonicBefore = {};
130 struct timespec raw = {};
131 struct timespec monotonicAfter = {};
132 passed &= posix_clock_gettime(CLOCK_MONOTONIC, output) == 0 &&
134 posix_clock_gettime(CLOCK_MONOTONIC_RAW, output) == 0 &&
136 posix_clock_gettime(CLOCK_MONOTONIC, pageEdge) == 0 &&
138 canonicalTimespec(monotonicBefore) && canonicalTimespec(raw) &&
139 canonicalTimespec(monotonicAfter) &&
140 (raw.tv_sec > monotonicBefore.tv_sec ||
141 (raw.tv_sec == monotonicBefore.tv_sec && raw.tv_nsec >= monotonicBefore.tv_nsec)) &&
142 (monotonicAfter.tv_sec > raw.tv_sec ||
143 (monotonicAfter.tv_sec == raw.tv_sec && monotonicAfter.tv_nsec >= raw.tv_nsec));
146 passed &= posix_clock_gettime(-1, bad) == -1 && thread->
getErrno() == Error::InvalidArgument;
149 posix_clock_gettime(CLOCK_REALTIME,
nullptr) == -1 && thread->
getErrno() == Error::BadAddress;
152 posix_clock_gettime(CLOCK_MONOTONIC, bad) == -1 && thread->
getErrno() == Error::BadAddress;
154 passed &= posix_clock_gettime(CLOCK_MONOTONIC_RAW, bad) == -1 &&
155 thread->
getErrno() == Error::BadAddress;
159 passed &= posix_clock_getres_native(CLOCK_REALTIME, output) == 0 &&
160 thread->
getErrno() == PreservedErrno &&
162 observed.tv_sec == 0 && observed.tv_nsec == 1;
164 passed &= posix_clock_getres(CLOCK_REALTIME, linuxOutput) == 0 &&
165 thread->
getErrno() == PreservedErrno &&
167 linuxObserved.tv_sec == 0 && linuxObserved.tv_nsec == 1;
170 posix_clock_getres(CLOCK_MONOTONIC,
nullptr) == 0 && thread->
getErrno() == PreservedErrno;
172 posix_clock_getres_native(CLOCK_MONOTONIC_RAW, output) == 0 &&
174 observed.tv_nsec == 1 && posix_clock_getres(CLOCK_MONOTONIC_RAW, linuxPageEdge) == 0 &&
176 linuxObserved.tv_sec == 0 && linuxObserved.tv_nsec == 1 &&
177 posix_clock_getres_native(CLOCK_MONOTONIC_RAW,
nullptr) == 0 &&
178 posix_clock_getres(CLOCK_MONOTONIC_RAW,
nullptr) == 0 && thread->
getErrno() == PreservedErrno;
180 passed &= posix_clock_getres(UnsupportedCoarseClock, linuxBad) == -1 &&
181 thread->
getErrno() == Error::InvalidArgument;
183 passed &= posix_clock_getres(CLOCK_MONOTONIC, linuxBad) == -1 &&
184 thread->
getErrno() == Error::BadAddress;
186 const struct timespec invalidRequests[] = {{-1, 0}, {0, -1}, {0, 1000000000}};
187 for (
size_t i = 0; passed && i <
sizeof(invalidRequests) /
sizeof(invalidRequests[0]); ++i) {
190 passed &= posix_nanosleep(input, bad) == -1 && thread->
getErrno() == Error::InvalidArgument;
194 passed &= posix_nanosleep(bad,
nullptr) == -1 && thread->
getErrno() == Error::BadAddress;
197 for (
size_t i = 0; passed && i <
sizeof(invalidLinuxRequests) /
sizeof(invalidLinuxRequests[0]);
200 sizeof(invalidLinuxRequests[i]));
202 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, 0, linuxInput, linuxBad) == -1 &&
203 thread->
getErrno() == Error::InvalidArgument;
206 passed &= posix_clock_nanosleep(UnsupportedCoarseClock, 0, linuxBad, linuxBad) == -1 &&
207 thread->
getErrno() == Error::InvalidArgument;
209 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC_RAW, 0, linuxBad, linuxBad) == -1 &&
210 thread->
getErrno() == Error::InvalidArgument;
212 passed &= posix_clock_settime(CLOCK_MONOTONIC_RAW, linuxBad) == -1 &&
213 thread->
getErrno() == Error::InvalidArgument;
215 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, 0, linuxBad,
nullptr) == -1 &&
216 thread->
getErrno() == Error::BadAddress;
218 const struct timespec zero = {0, 0};
220 const size_t alarmsBeforeZero = Time::getHostedAlarmCreateCount();
222 passed &= posix_nanosleep(input, bad) == 0 && thread->
getErrno() == PreservedErrno &&
223 Time::getHostedAlarmCreateCount() == alarmsBeforeZero;
225 const struct timespec oneNanosecond = {0, 1};
228 passed &= posix_nanosleep(pageEdge, bad) == 0 && thread->
getErrno() == PreservedErrno;
232 UntouchedRemainderNanoseconds};
236 const size_t alarmsBeforeRelativeClockSleep = Time::getHostedAlarmCreateCount();
238 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, IgnoredClockNanosleepFlag, linuxInput,
240 thread->
getErrno() == PreservedErrno &&
241 Time::getHostedAlarmCreateCount() == alarmsBeforeRelativeClockSleep + 1 &&
243 linuxObserved.tv_sec == untouchedRemainder.tv_sec &&
244 linuxObserved.tv_nsec == untouchedRemainder.tv_nsec;
246 const size_t alarmsBeforePastDeadline = Time::getHostedAlarmCreateCount();
248 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, linuxInput, linuxBad) == 0 &&
249 thread->
getErrno() == PreservedErrno &&
250 Time::getHostedAlarmCreateCount() == alarmsBeforePastDeadline;
252 const Time::Timestamp futureDeadline = Time::getTicks() + (2 * Time::Multiplier::Millisecond);
254 static_cast<int64_t
>(futureDeadline / Time::Multiplier::Second),
255 static_cast<int64_t
>(futureDeadline % Time::Multiplier::Second)};
259 const size_t alarmsBeforeFutureDeadline = Time::getHostedAlarmCreateCount();
261 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, linuxInput, linuxOutput) == 0 &&
262 thread->
getErrno() == PreservedErrno && Time::getTicks() >= futureDeadline &&
263 Time::getHostedAlarmCreateCount() >= alarmsBeforeFutureDeadline + 1 &&
265 linuxObserved.tv_sec == untouchedRemainder.tv_sec &&
266 linuxObserved.tv_nsec == untouchedRemainder.tv_nsec;
270 g_SleepSignalCalls = 0;
271 SignalEvent expiryCollision(
reinterpret_cast<uintptr_t
>(&sleepSignalHandler), SleepSignal);
272 const bool collisionQueued = thread->
sendEvent(&expiryCollision);
274 const int collisionResult = posix_nanosleep(pageEdge, bad);
275 const bool collisionStillQueued = thread->
hasEvent(&expiryCollision);
276 if (collisionStillQueued) {
279 passed &= collisionQueued && collisionResult == 0 && !collisionStillQueued &&
280 g_SleepSignalCalls == 1 && thread->
getErrno() == PreservedErrno &&
281 thread->getInterruptionReason() == Thread::NotInterrupted;
283 g_SleepSignalCalls = 0;
284 SignalEvent clockExpiryCollision(
reinterpret_cast<uintptr_t
>(&sleepSignalHandler), SleepSignal);
285 const bool clockCollisionQueued = thread->
sendEvent(&clockExpiryCollision);
287 const int clockCollisionResult = posix_clock_nanosleep(CLOCK_MONOTONIC, 0, linuxInput, linuxBad);
288 const bool clockCollisionStillQueued = thread->
hasEvent(&clockExpiryCollision);
289 if (clockCollisionStillQueued) {
290 thread->
cullEvent(&clockExpiryCollision);
292 passed &= clockCollisionQueued && clockCollisionResult == 0 && !clockCollisionStillQueued &&
293 g_SleepSignalCalls == 1 && thread->
getErrno() == PreservedErrno &&
294 thread->getInterruptionReason() == Thread::NotInterrupted;
297 MemoryMappedObject::Read) == 1;
299 passed &= posix_clock_gettime(CLOCK_REALTIME, readOnly) == -1 &&
300 thread->
getErrno() == Error::BadAddress;
302 passed &= posix_clock_getres(CLOCK_REALTIME, linuxReadOnly) == -1 &&
303 thread->
getErrno() == Error::BadAddress;
305 passed &= posix_clock_gettime(CLOCK_MONOTONIC_RAW, readOnly) == -1 &&
306 thread->
getErrno() == Error::BadAddress;
308 passed &= posix_clock_getres(CLOCK_MONOTONIC_RAW, linuxReadOnly) == -1 &&
309 thread->
getErrno() == Error::BadAddress;
311 const bool middleRemoved =
313 passed &= middleRemoved;
315 passed &= posix_nanosleep(pageEdge,
nullptr) == -1 && thread->
getErrno() == Error::BadAddress;
317 passed &= posix_clock_gettime(CLOCK_MONOTONIC, pageEdge) == -1 &&
318 thread->
getErrno() == Error::BadAddress;
320 passed &= posix_clock_nanosleep(CLOCK_REALTIME, 0, linuxPageEdge,
nullptr) == -1 &&
321 thread->
getErrno() == Error::BadAddress;
325 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
327 context->passed = passed;
328 context->returned += 1;
329 return passed ? 0 : 1;
332enum RemainderTarget { ValidRemainder, InvalidRemainder };
333enum SleepCall { NanosleepCall, RelativeClockNanosleepCall, AbsoluteClockNanosleepCall };
335struct InterruptedSleepContext {
336 InterruptedSleepContext(
struct timespec request, RemainderTarget target,
337 SleepCall call = NanosleepCall, clockid_t clockId = CLOCK_MONOTONIC)
350 struct timespec request;
351 RemainderTarget target;
360 struct timespec remaining;
363int exerciseInterruptedSleep(
void* parameter) {
364 InterruptedSleepContext* context =
reinterpret_cast<InterruptedSleepContext*
>(parameter);
369 uintptr_t address = 0;
370 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
371 uintptr_t mappedAddress = address;
374 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
376 if (!mapping || mappedAddress != address) {
381 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
383 context->returned += 1;
387 struct timespec* input =
reinterpret_cast<struct timespec*
>(address + 64);
388 struct timespec* output =
reinterpret_cast<struct timespec*
>(address + 256);
390 struct timespec* remainder =
391 context->target == ValidRemainder ? output :
reinterpret_cast<struct timespec*
>(kernelStart);
392 struct timespec syscallRequest = context->request;
393 if (context->call == AbsoluteClockNanosleepCall) {
394 const Time::Timestamp duration =
395 static_cast<Time::Timestamp
>(context->request.tv_sec) * Time::Multiplier::Second +
396 static_cast<Time::Timestamp
>(context->request.tv_nsec);
397 const Time::Timestamp deadline =
398 (context->clockId == CLOCK_REALTIME ? Time::getTimeNanoseconds() : Time::getTicks()) +
400 syscallRequest.tv_sec =
static_cast<time_t
>(deadline / Time::Multiplier::Second);
401 syscallRequest.tv_nsec =
static_cast<long>(deadline % Time::Multiplier::Second);
404 const struct timespec untouchedRemainder = {UntouchedRemainderSeconds,
405 UntouchedRemainderNanoseconds};
407 (context->target == ValidRemainder &&
410 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
411 context->returned += 1;
415 context->mapped += 1;
418 if (context->call == NanosleepCall) {
419 context->result = posix_nanosleep(input, remainder);
421 context->result = posix_clock_nanosleep(
422 context->clockId, context->call == AbsoluteClockNanosleepCall ? TIMER_ABSTIME : 0,
426 context->error = thread->
getErrno();
427 if (context->target == ValidRemainder &&
429 context->copiedRemainder += 1;
433 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
434 context->returned += 1;
438bool waitForSleepBlock(
Thread* thread, InterruptedSleepContext& context) {
439 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
440 while (Time::getTicks() < deadline) {
442 uintptr_t debugAddress = 0;
443 if (context.ready && thread->
getWaitDebugInfo(info) && info.queue && info.queued &&
447 if (context.returned) {
455bool waitForSleepReturn(InterruptedSleepContext& context) {
456 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
457 while (Time::getTicks() < deadline) {
458 if (context.returned) {
466bool interruptedSleep(
Process* kernelProcess,
const struct timespec& request,
467 RemainderTarget target,
bool expectSaturation, SleepCall call = NanosleepCall,
468 clockid_t clockId = CLOCK_MONOTONIC) {
471 InterruptedSleepContext context(request, target, call, clockId);
473 new Thread(process, exerciseInterruptedSleep, &context,
nullptr,
false,
true,
true);
474 if (expectSaturation) {
475 worker->setName(
"hosted saturated nanosleep worker");
477 worker->setName(
"hosted interrupted nanosleep worker");
481 g_SleepSignalCalls = 0;
482 const bool started =
worker->start();
483 const bool blocked = started && waitForSleepBlock(
worker, context);
485 SleepSignal, ~0UL, 0,
true,
true);
486 const bool signalQueued = started &&
worker->sendEvent(signal);
491 const bool returned = started && waitForSleepReturn(context);
492 if (started && !returned) {
495 const bool joined = started &&
worker->joinForCompletion();
500 bool validResult =
false;
501 if (call == AbsoluteClockNanosleepCall) {
503 context.result == -1 && context.error == Error::Interrupted &&
504 (target == InvalidRemainder ||
505 (context.copiedRemainder && context.remaining.tv_sec == UntouchedRemainderSeconds &&
506 context.remaining.tv_nsec == UntouchedRemainderNanoseconds));
507 }
else if (target == InvalidRemainder) {
509 context.result == -1 && context.error == Error::BadAddress && !context.copiedRemainder;
511 validResult = context.result == -1 && context.error == Error::Interrupted &&
512 context.copiedRemainder && canonicalTimespec(context.remaining) &&
513 (context.remaining.tv_sec > 0 || context.remaining.tv_nsec > 0) &&
514 (context.remaining.tv_sec < request.tv_sec ||
515 (context.remaining.tv_sec == request.tv_sec &&
516 context.remaining.tv_nsec <= request.tv_nsec));
518 const bool saturated =
519 !expectSaturation || (context.remaining.tv_sec <= MaximumLinuxSleepSeconds &&
520 context.remaining.tv_sec >= MaximumLinuxSleepSeconds - 5);
521 const bool passed = started && blocked && signalQueued && returned && joined &&
522 context.mapped == 1 && context.returned == 1 && g_SleepSignalCalls == 1 &&
523 validResult && saturated;
529bool runHostedSleepClockSyscallRegressions(
Process* kernelProcess) {
532 ValidationContext validation;
534 new Thread(process, exerciseSleepClockValidation, &validation,
nullptr,
false,
true,
true);
535 worker->setName(
"hosted sleep and clock syscall validation worker");
538 const bool started =
worker->start();
539 const bool joined = started &&
worker->joinForCompletion();
543 bool passed = started && joined && validation.returned == 1 && validation.passed;
546 const struct timespec ordinaryRequest = {5, 0};
547 const struct timespec hugeRequest = {
static_cast<time_t
>(0x7FFFFFFFFFFFFFFFLL), 999999999};
548 passed &= interruptedSleep(kernelProcess, ordinaryRequest, ValidRemainder,
false);
549 passed &= interruptedSleep(kernelProcess, ordinaryRequest, InvalidRemainder,
false);
550 passed &= interruptedSleep(kernelProcess, hugeRequest, ValidRemainder,
true);
551 passed &= interruptedSleep(kernelProcess, ordinaryRequest, ValidRemainder,
false,
552 RelativeClockNanosleepCall, CLOCK_MONOTONIC);
553 passed &= interruptedSleep(kernelProcess, ordinaryRequest, InvalidRemainder,
false,
554 RelativeClockNanosleepCall, CLOCK_REALTIME);
555 passed &= interruptedSleep(kernelProcess, hugeRequest, ValidRemainder,
true,
556 RelativeClockNanosleepCall, CLOCK_MONOTONIC);
557 passed &= interruptedSleep(kernelProcess, ordinaryRequest, ValidRemainder,
false,
558 AbsoluteClockNanosleepCall, CLOCK_MONOTONIC);
559 passed &= interruptedSleep(kernelProcess, ordinaryRequest, InvalidRemainder,
false,
560 AbsoluteClockNanosleepCall, CLOCK_REALTIME);
564 "HOSTED-SYSCALL-TEST: FAIL sleep-clock-usercopy: "
565 "Linux timespec validation, saturation, interruption, or usercopy behavior regressed");
568 NOTICE(
"HOSTED-SYSCALL-TEST: PASS sleep-clock-usercopy");
Memory-mapped file interface.
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
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)
static ProcessorInformation & information()
static Scheduler & instance()
void setErrno(size_t err)
@ TerminateThread
Exit only this thread during Process exit.
bool getWaitDebugInfo(WaitDebugInfo &info)
bool hasEvent(Event *pEvent)
void cullEvent(Event *pEvent)
DebugState getDebugState(uintptr_t &address)
Process * getParent() const
bool sendEvent(Event *pEvent)