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;
327 passed &= posix_nanosleep(pageEdge,
nullptr,
true) == 0 && thread->
getErrno() == PreservedErrno;
331 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
333 context->passed = passed;
334 context->returned += 1;
335 return passed ? 0 : 1;
338enum RemainderTarget { ValidRemainder, InvalidRemainder };
339enum SleepCall { NanosleepCall, RelativeClockNanosleepCall, AbsoluteClockNanosleepCall };
341struct InterruptedSleepContext {
342 InterruptedSleepContext(
struct timespec request, RemainderTarget target,
343 SleepCall call = NanosleepCall, clockid_t clockId = CLOCK_MONOTONIC)
356 struct timespec request;
357 RemainderTarget target;
366 struct timespec remaining;
369int exerciseInterruptedSleep(
void* parameter) {
370 InterruptedSleepContext* context =
reinterpret_cast<InterruptedSleepContext*
>(parameter);
375 uintptr_t address = 0;
376 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
377 uintptr_t mappedAddress = address;
380 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
382 if (!mapping || mappedAddress != address) {
387 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
389 context->returned += 1;
393 struct timespec* input =
reinterpret_cast<struct timespec*
>(address + 64);
394 struct timespec* output =
reinterpret_cast<struct timespec*
>(address + 256);
396 struct timespec* remainder =
397 context->target == ValidRemainder ? output :
reinterpret_cast<struct timespec*
>(kernelStart);
398 struct timespec syscallRequest = context->request;
399 if (context->call == AbsoluteClockNanosleepCall) {
400 const Time::Timestamp duration =
401 static_cast<Time::Timestamp
>(context->request.tv_sec) * Time::Multiplier::Second +
402 static_cast<Time::Timestamp
>(context->request.tv_nsec);
403 const Time::Timestamp deadline =
404 (context->clockId == CLOCK_REALTIME ? Time::getTimeNanoseconds() : Time::getTicks()) +
406 syscallRequest.tv_sec =
static_cast<time_t
>(deadline / Time::Multiplier::Second);
407 syscallRequest.tv_nsec =
static_cast<long>(deadline % Time::Multiplier::Second);
410 const struct timespec untouchedRemainder = {UntouchedRemainderSeconds,
411 UntouchedRemainderNanoseconds};
413 (context->target == ValidRemainder &&
416 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
417 context->returned += 1;
421 context->mapped += 1;
424 if (context->call == NanosleepCall) {
425 context->result = posix_nanosleep(input, remainder);
427 context->result = posix_clock_nanosleep(
428 context->clockId, context->call == AbsoluteClockNanosleepCall ? TIMER_ABSTIME : 0,
432 context->error = thread->
getErrno();
433 if (context->target == ValidRemainder &&
435 context->copiedRemainder += 1;
439 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
440 context->returned += 1;
444bool waitForSleepBlock(
Thread* thread, InterruptedSleepContext& context) {
445 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
446 while (Time::getTicks() < deadline) {
448 uintptr_t debugAddress = 0;
449 if (context.ready && thread->
getWaitDebugInfo(info) && info.queue && info.queued &&
453 if (context.returned) {
461bool waitForSleepReturn(InterruptedSleepContext& context) {
462 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
463 while (Time::getTicks() < deadline) {
464 if (context.returned) {
472bool interruptedSleep(
Process* kernelProcess,
const struct timespec& request,
473 RemainderTarget target,
bool expectSaturation, SleepCall call = NanosleepCall,
474 clockid_t clockId = CLOCK_MONOTONIC) {
477 InterruptedSleepContext context(request, target, call, clockId);
479 new Thread(process, exerciseInterruptedSleep, &context,
nullptr,
false,
true,
true);
480 if (expectSaturation) {
481 worker->setName(
"hosted saturated nanosleep worker");
483 worker->setName(
"hosted interrupted nanosleep worker");
487 g_SleepSignalCalls = 0;
488 const bool started =
worker->start();
489 const bool blocked = started && waitForSleepBlock(
worker, context);
491 SleepSignal, ~0UL, 0,
true,
true);
492 const bool signalQueued = started &&
worker->sendEvent(signal);
497 const bool returned = started && waitForSleepReturn(context);
498 if (started && !returned) {
501 const bool joined = started &&
worker->joinForCompletion();
506 bool validResult =
false;
507 if (call == AbsoluteClockNanosleepCall) {
509 context.result == -1 && context.error == Error::Interrupted &&
510 (target == InvalidRemainder ||
511 (context.copiedRemainder && context.remaining.tv_sec == UntouchedRemainderSeconds &&
512 context.remaining.tv_nsec == UntouchedRemainderNanoseconds));
513 }
else if (target == InvalidRemainder) {
515 context.result == -1 && context.error == Error::BadAddress && !context.copiedRemainder;
517 validResult = context.result == -1 && context.error == Error::Interrupted &&
518 context.copiedRemainder && canonicalTimespec(context.remaining) &&
519 (context.remaining.tv_sec > 0 || context.remaining.tv_nsec > 0) &&
520 (context.remaining.tv_sec < request.tv_sec ||
521 (context.remaining.tv_sec == request.tv_sec &&
522 context.remaining.tv_nsec <= request.tv_nsec));
524 const bool saturated =
525 !expectSaturation || (context.remaining.tv_sec <= MaximumLinuxSleepSeconds &&
526 context.remaining.tv_sec >= MaximumLinuxSleepSeconds - 5);
527 const bool passed = started && blocked && signalQueued && returned && joined &&
528 context.mapped == 1 && context.returned == 1 && g_SleepSignalCalls == 1 &&
529 validResult && saturated;
535bool runHostedSleepClockSyscallRegressions(
Process* kernelProcess) {
538 ValidationContext validation;
540 new Thread(process, exerciseSleepClockValidation, &validation,
nullptr,
false,
true,
true);
541 worker->setName(
"hosted sleep and clock syscall validation worker");
544 const bool started =
worker->start();
545 const bool joined = started &&
worker->joinForCompletion();
549 bool passed = started && joined && validation.returned == 1 && validation.passed;
552 const struct timespec ordinaryRequest = {5, 0};
553 const struct timespec hugeRequest = {
static_cast<time_t
>(0x7FFFFFFFFFFFFFFFLL), 999999999};
554 passed &= interruptedSleep(kernelProcess, ordinaryRequest, ValidRemainder,
false);
555 passed &= interruptedSleep(kernelProcess, ordinaryRequest, InvalidRemainder,
false);
556 passed &= interruptedSleep(kernelProcess, hugeRequest, ValidRemainder,
true);
557 passed &= interruptedSleep(kernelProcess, ordinaryRequest, ValidRemainder,
false,
558 RelativeClockNanosleepCall, CLOCK_MONOTONIC);
559 passed &= interruptedSleep(kernelProcess, ordinaryRequest, InvalidRemainder,
false,
560 RelativeClockNanosleepCall, CLOCK_REALTIME);
561 passed &= interruptedSleep(kernelProcess, hugeRequest, ValidRemainder,
true,
562 RelativeClockNanosleepCall, CLOCK_MONOTONIC);
563 passed &= interruptedSleep(kernelProcess, ordinaryRequest, ValidRemainder,
false,
564 AbsoluteClockNanosleepCall, CLOCK_MONOTONIC);
565 passed &= interruptedSleep(kernelProcess, ordinaryRequest, InvalidRemainder,
false,
566 AbsoluteClockNanosleepCall, CLOCK_REALTIME);
570 "HOSTED-SYSCALL-TEST: FAIL sleep-clock-usercopy: "
571 "Linux timespec validation, saturation, interruption, or usercopy behavior regressed");
574 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)