The Pedigree Project 0.1
sleep-clock-syscall-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 <stddef.h>
21#include <stdint.h>
22#include <time.h>
23
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"
29
30extern "C" Time::Timestamp posixNanosleepAlarmDurationForTest(time_t seconds, long nanoseconds);
31
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);
37
38namespace {
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));
51
52Atomic<size_t> g_SleepSignalCalls(0);
53
54void sleepSignalHandler(size_t) {
55 g_SleepSignalCalls += 1;
56}
57
58bool canonicalTimespec(const struct timespec& value) {
59 return value.tv_sec >= 0 && value.tv_nsec >= 0 && value.tv_nsec < 1000000000;
60}
61
62struct ValidationContext {
63 ValidationContext() : passed(false), returned(0) {}
64
65 bool passed;
66 Atomic<size_t> returned;
67};
68
69int exerciseSleepClockValidation(void* parameter) {
70 ValidationContext* context = reinterpret_cast<ValidationContext*>(parameter);
71 Thread* thread = Processor::information().getCurrentThread();
72 PosixProcess* process = static_cast<PosixProcess*>(thread->getParent());
73 bool passed = process->getType() == Process::Posix;
74
75 const size_t pageSize = PhysicalMemoryManager::getPageSize();
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;
81 MemoryMappedObject* mapping =
82 allocated
84 mappedAddress, mappingLength, MemoryMappedObject::Read | MemoryMappedObject::Write)
85 : nullptr;
86 if (!mapping || mappedAddress != address) {
87 if (mapping) {
88 MemoryMapManager::instance().remove(mappedAddress, mappingLength);
89 }
90 if (allocated) {
91 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
92 }
93 context->returned += 1;
94 return 1;
95 }
96
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);
102 const uintptr_t kernelStart = Processor::information().getVirtualAddressSpace().getKernelStart();
103 struct timespec* bad = reinterpret_cast<struct timespec*>(kernelStart);
104 LinuxKernelTimespec* linuxInput = reinterpret_cast<LinuxKernelTimespec*>(input);
105 LinuxKernelTimespec* linuxOutput = reinterpret_cast<LinuxKernelTimespec*>(output);
106 LinuxKernelTimespec* linuxPageEdge = reinterpret_cast<LinuxKernelTimespec*>(pageEdge);
107 LinuxKernelTimespec* linuxReadOnly = reinterpret_cast<LinuxKernelTimespec*>(readOnly);
108 LinuxKernelTimespec* linuxBad = reinterpret_cast<LinuxKernelTimespec*>(bad);
109
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;
121
122 struct timespec observed = {};
123 thread->setErrno(PreservedErrno);
124 passed &= posix_clock_gettime(CLOCK_REALTIME, output) == 0 &&
125 thread->getErrno() == PreservedErrno &&
126 PosixSubsystem::copyFromUser(&observed, output, sizeof(observed)) &&
127 canonicalTimespec(observed);
128
129 struct timespec monotonicBefore = {};
130 struct timespec raw = {};
131 struct timespec monotonicAfter = {};
132 passed &= posix_clock_gettime(CLOCK_MONOTONIC, output) == 0 &&
133 PosixSubsystem::copyFromUser(&monotonicBefore, output, sizeof(monotonicBefore)) &&
134 posix_clock_gettime(CLOCK_MONOTONIC_RAW, output) == 0 &&
135 PosixSubsystem::copyFromUser(&raw, output, sizeof(raw)) &&
136 posix_clock_gettime(CLOCK_MONOTONIC, pageEdge) == 0 &&
137 PosixSubsystem::copyFromUser(&monotonicAfter, pageEdge, sizeof(monotonicAfter)) &&
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));
144
145 thread->setErrno(0);
146 passed &= posix_clock_gettime(-1, bad) == -1 && thread->getErrno() == Error::InvalidArgument;
147 thread->setErrno(0);
148 passed &=
149 posix_clock_gettime(CLOCK_REALTIME, nullptr) == -1 && thread->getErrno() == Error::BadAddress;
150 thread->setErrno(0);
151 passed &=
152 posix_clock_gettime(CLOCK_MONOTONIC, bad) == -1 && thread->getErrno() == Error::BadAddress;
153 thread->setErrno(0);
154 passed &= posix_clock_gettime(CLOCK_MONOTONIC_RAW, bad) == -1 &&
155 thread->getErrno() == Error::BadAddress;
156
157 LinuxKernelTimespec linuxObserved = {};
158 thread->setErrno(PreservedErrno);
159 passed &= posix_clock_getres_native(CLOCK_REALTIME, output) == 0 &&
160 thread->getErrno() == PreservedErrno &&
161 PosixSubsystem::copyFromUser(&observed, output, sizeof(observed)) &&
162 observed.tv_sec == 0 && observed.tv_nsec == 1;
163 thread->setErrno(PreservedErrno);
164 passed &= posix_clock_getres(CLOCK_REALTIME, linuxOutput) == 0 &&
165 thread->getErrno() == PreservedErrno &&
166 PosixSubsystem::copyFromUser(&linuxObserved, linuxOutput, sizeof(linuxObserved)) &&
167 linuxObserved.tv_sec == 0 && linuxObserved.tv_nsec == 1;
168 thread->setErrno(PreservedErrno);
169 passed &=
170 posix_clock_getres(CLOCK_MONOTONIC, nullptr) == 0 && thread->getErrno() == PreservedErrno;
171 passed &=
172 posix_clock_getres_native(CLOCK_MONOTONIC_RAW, output) == 0 &&
173 PosixSubsystem::copyFromUser(&observed, output, sizeof(observed)) && observed.tv_sec == 0 &&
174 observed.tv_nsec == 1 && posix_clock_getres(CLOCK_MONOTONIC_RAW, linuxPageEdge) == 0 &&
175 PosixSubsystem::copyFromUser(&linuxObserved, linuxPageEdge, sizeof(linuxObserved)) &&
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;
179 thread->setErrno(0);
180 passed &= posix_clock_getres(UnsupportedCoarseClock, linuxBad) == -1 &&
181 thread->getErrno() == Error::InvalidArgument;
182 thread->setErrno(0);
183 passed &= posix_clock_getres(CLOCK_MONOTONIC, linuxBad) == -1 &&
184 thread->getErrno() == Error::BadAddress;
185
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) {
188 passed &= PosixSubsystem::copyToUser(input, &invalidRequests[i], sizeof(invalidRequests[i]));
189 thread->setErrno(0);
190 passed &= posix_nanosleep(input, bad) == -1 && thread->getErrno() == Error::InvalidArgument;
191 }
192
193 thread->setErrno(0);
194 passed &= posix_nanosleep(bad, nullptr) == -1 && thread->getErrno() == Error::BadAddress;
195
196 const LinuxKernelTimespec invalidLinuxRequests[] = {{-1, 0}, {0, -1}, {0, 1000000000}};
197 for (size_t i = 0; passed && i < sizeof(invalidLinuxRequests) / sizeof(invalidLinuxRequests[0]);
198 ++i) {
199 passed &= PosixSubsystem::copyToUser(linuxInput, &invalidLinuxRequests[i],
200 sizeof(invalidLinuxRequests[i]));
201 thread->setErrno(0);
202 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, 0, linuxInput, linuxBad) == -1 &&
203 thread->getErrno() == Error::InvalidArgument;
204 }
205 thread->setErrno(0);
206 passed &= posix_clock_nanosleep(UnsupportedCoarseClock, 0, linuxBad, linuxBad) == -1 &&
207 thread->getErrno() == Error::InvalidArgument;
208 thread->setErrno(0);
209 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC_RAW, 0, linuxBad, linuxBad) == -1 &&
210 thread->getErrno() == Error::InvalidArgument;
211 thread->setErrno(0);
212 passed &= posix_clock_settime(CLOCK_MONOTONIC_RAW, linuxBad) == -1 &&
213 thread->getErrno() == Error::InvalidArgument;
214 thread->setErrno(0);
215 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, 0, linuxBad, nullptr) == -1 &&
216 thread->getErrno() == Error::BadAddress;
217
218 const struct timespec zero = {0, 0};
219 passed &= PosixSubsystem::copyToUser(input, &zero, sizeof(zero));
220 const size_t alarmsBeforeZero = Time::getHostedAlarmCreateCount();
221 thread->setErrno(PreservedErrno);
222 passed &= posix_nanosleep(input, bad) == 0 && thread->getErrno() == PreservedErrno &&
223 Time::getHostedAlarmCreateCount() == alarmsBeforeZero;
224
225 const struct timespec oneNanosecond = {0, 1};
226 passed &= PosixSubsystem::copyToUser(pageEdge, &oneNanosecond, sizeof(oneNanosecond));
227 thread->setErrno(PreservedErrno);
228 passed &= posix_nanosleep(pageEdge, bad) == 0 && thread->getErrno() == PreservedErrno;
229
230 const LinuxKernelTimespec linuxOneNanosecond = {0, 1};
231 const LinuxKernelTimespec untouchedRemainder = {UntouchedRemainderSeconds,
232 UntouchedRemainderNanoseconds};
233 passed &=
234 PosixSubsystem::copyToUser(linuxInput, &linuxOneNanosecond, sizeof(linuxOneNanosecond)) &&
235 PosixSubsystem::copyToUser(linuxOutput, &untouchedRemainder, sizeof(untouchedRemainder));
236 const size_t alarmsBeforeRelativeClockSleep = Time::getHostedAlarmCreateCount();
237 thread->setErrno(PreservedErrno);
238 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, IgnoredClockNanosleepFlag, linuxInput,
239 linuxOutput) == 0 &&
240 thread->getErrno() == PreservedErrno &&
241 Time::getHostedAlarmCreateCount() == alarmsBeforeRelativeClockSleep + 1 &&
242 PosixSubsystem::copyFromUser(&linuxObserved, linuxOutput, sizeof(linuxObserved)) &&
243 linuxObserved.tv_sec == untouchedRemainder.tv_sec &&
244 linuxObserved.tv_nsec == untouchedRemainder.tv_nsec;
245
246 const size_t alarmsBeforePastDeadline = Time::getHostedAlarmCreateCount();
247 thread->setErrno(PreservedErrno);
248 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, linuxInput, linuxBad) == 0 &&
249 thread->getErrno() == PreservedErrno &&
250 Time::getHostedAlarmCreateCount() == alarmsBeforePastDeadline;
251
252 const Time::Timestamp futureDeadline = Time::getTicks() + (2 * Time::Multiplier::Millisecond);
253 const LinuxKernelTimespec futureDeadlineValue = {
254 static_cast<int64_t>(futureDeadline / Time::Multiplier::Second),
255 static_cast<int64_t>(futureDeadline % Time::Multiplier::Second)};
256 passed &=
257 PosixSubsystem::copyToUser(linuxInput, &futureDeadlineValue, sizeof(futureDeadlineValue)) &&
258 PosixSubsystem::copyToUser(linuxOutput, &untouchedRemainder, sizeof(untouchedRemainder));
259 const size_t alarmsBeforeFutureDeadline = Time::getHostedAlarmCreateCount();
260 thread->setErrno(PreservedErrno);
261 passed &= posix_clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, linuxInput, linuxOutput) == 0 &&
262 thread->getErrno() == PreservedErrno && Time::getTicks() >= futureDeadline &&
263 Time::getHostedAlarmCreateCount() >= alarmsBeforeFutureDeadline + 1 &&
264 PosixSubsystem::copyFromUser(&linuxObserved, linuxOutput, sizeof(linuxObserved)) &&
265 linuxObserved.tv_sec == untouchedRemainder.tv_sec &&
266 linuxObserved.tv_nsec == untouchedRemainder.tv_nsec;
267
268 passed &= PosixSubsystem::copyToUser(linuxInput, &linuxOneNanosecond, sizeof(linuxOneNanosecond));
269
270 g_SleepSignalCalls = 0;
271 SignalEvent expiryCollision(reinterpret_cast<uintptr_t>(&sleepSignalHandler), SleepSignal);
272 const bool collisionQueued = thread->sendEvent(&expiryCollision);
273 thread->setErrno(PreservedErrno);
274 const int collisionResult = posix_nanosleep(pageEdge, bad);
275 const bool collisionStillQueued = thread->hasEvent(&expiryCollision);
276 if (collisionStillQueued) {
277 thread->cullEvent(&expiryCollision);
278 }
279 passed &= collisionQueued && collisionResult == 0 && !collisionStillQueued &&
280 g_SleepSignalCalls == 1 && thread->getErrno() == PreservedErrno &&
281 thread->getInterruptionReason() == Thread::NotInterrupted;
282
283 g_SleepSignalCalls = 0;
284 SignalEvent clockExpiryCollision(reinterpret_cast<uintptr_t>(&sleepSignalHandler), SleepSignal);
285 const bool clockCollisionQueued = thread->sendEvent(&clockExpiryCollision);
286 thread->setErrno(PreservedErrno);
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);
291 }
292 passed &= clockCollisionQueued && clockCollisionResult == 0 && !clockCollisionStillQueued &&
293 g_SleepSignalCalls == 1 && thread->getErrno() == PreservedErrno &&
294 thread->getInterruptionReason() == Thread::NotInterrupted;
295
296 passed &= MemoryMapManager::instance().setPermissions(address + (pageSize * 2), pageSize,
297 MemoryMappedObject::Read) == 1;
298 thread->setErrno(0);
299 passed &= posix_clock_gettime(CLOCK_REALTIME, readOnly) == -1 &&
300 thread->getErrno() == Error::BadAddress;
301 thread->setErrno(0);
302 passed &= posix_clock_getres(CLOCK_REALTIME, linuxReadOnly) == -1 &&
303 thread->getErrno() == Error::BadAddress;
304 thread->setErrno(0);
305 passed &= posix_clock_gettime(CLOCK_MONOTONIC_RAW, readOnly) == -1 &&
306 thread->getErrno() == Error::BadAddress;
307 thread->setErrno(0);
308 passed &= posix_clock_getres(CLOCK_MONOTONIC_RAW, linuxReadOnly) == -1 &&
309 thread->getErrno() == Error::BadAddress;
310
311 const bool middleRemoved =
312 mapping && MemoryMapManager::instance().remove(address + pageSize, pageSize) == 1;
313 passed &= middleRemoved;
314 thread->setErrno(0);
315 passed &= posix_nanosleep(pageEdge, nullptr) == -1 && thread->getErrno() == Error::BadAddress;
316 thread->setErrno(0);
317 passed &= posix_clock_gettime(CLOCK_MONOTONIC, pageEdge) == -1 &&
318 thread->getErrno() == Error::BadAddress;
319 thread->setErrno(0);
320 passed &= posix_clock_nanosleep(CLOCK_REALTIME, 0, linuxPageEdge, nullptr) == -1 &&
321 thread->getErrno() == Error::BadAddress;
322
323 MemoryMapManager::instance().remove(address, pageSize);
324 MemoryMapManager::instance().remove(address + (pageSize * 2), pageSize);
325 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
326
327 context->passed = passed;
328 context->returned += 1;
329 return passed ? 0 : 1;
330}
331
332enum RemainderTarget { ValidRemainder, InvalidRemainder };
333enum SleepCall { NanosleepCall, RelativeClockNanosleepCall, AbsoluteClockNanosleepCall };
334
335struct InterruptedSleepContext {
336 InterruptedSleepContext(struct timespec request, RemainderTarget target,
337 SleepCall call = NanosleepCall, clockid_t clockId = CLOCK_MONOTONIC)
338 : request(request),
339 target(target),
340 call(call),
341 clockId(clockId),
342 ready(0),
343 returned(0),
344 mapped(0),
345 copiedRemainder(0),
346 result(0),
347 error(0),
348 remaining{} {}
349
350 struct timespec request;
351 RemainderTarget target;
352 SleepCall call;
353 clockid_t clockId;
354 Atomic<size_t> ready;
355 Atomic<size_t> returned;
356 Atomic<size_t> mapped;
357 Atomic<size_t> copiedRemainder;
358 int result;
359 size_t error;
360 struct timespec remaining;
361};
362
363int exerciseInterruptedSleep(void* parameter) {
364 InterruptedSleepContext* context = reinterpret_cast<InterruptedSleepContext*>(parameter);
365 Thread* thread = Processor::information().getCurrentThread();
366 PosixProcess* process = static_cast<PosixProcess*>(thread->getParent());
367
368 const size_t pageSize = PhysicalMemoryManager::getPageSize();
369 uintptr_t address = 0;
370 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
371 uintptr_t mappedAddress = address;
372 MemoryMappedObject* mapping =
374 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
375 : nullptr;
376 if (!mapping || mappedAddress != address) {
377 if (mapping) {
378 MemoryMapManager::instance().remove(mappedAddress, pageSize);
379 }
380 if (allocated) {
381 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
382 }
383 context->returned += 1;
384 return 1;
385 }
386
387 struct timespec* input = reinterpret_cast<struct timespec*>(address + 64);
388 struct timespec* output = reinterpret_cast<struct timespec*>(address + 256);
389 const uintptr_t kernelStart = Processor::information().getVirtualAddressSpace().getKernelStart();
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()) +
399 duration;
400 syscallRequest.tv_sec = static_cast<time_t>(deadline / Time::Multiplier::Second);
401 syscallRequest.tv_nsec = static_cast<long>(deadline % Time::Multiplier::Second);
402 }
403
404 const struct timespec untouchedRemainder = {UntouchedRemainderSeconds,
405 UntouchedRemainderNanoseconds};
406 if (!PosixSubsystem::copyToUser(input, &syscallRequest, sizeof(syscallRequest)) ||
407 (context->target == ValidRemainder &&
408 !PosixSubsystem::copyToUser(output, &untouchedRemainder, sizeof(untouchedRemainder)))) {
409 MemoryMapManager::instance().remove(address, pageSize);
410 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
411 context->returned += 1;
412 return 1;
413 }
414
415 context->mapped += 1;
416 context->ready += 1;
417 thread->setErrno(PreservedErrno);
418 if (context->call == NanosleepCall) {
419 context->result = posix_nanosleep(input, remainder);
420 } else {
421 context->result = posix_clock_nanosleep(
422 context->clockId, context->call == AbsoluteClockNanosleepCall ? TIMER_ABSTIME : 0,
423 reinterpret_cast<LinuxKernelTimespec*>(input),
424 reinterpret_cast<LinuxKernelTimespec*>(remainder));
425 }
426 context->error = thread->getErrno();
427 if (context->target == ValidRemainder &&
428 PosixSubsystem::copyFromUser(&context->remaining, output, sizeof(context->remaining))) {
429 context->copiedRemainder += 1;
430 }
431
432 MemoryMapManager::instance().remove(address, pageSize);
433 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
434 context->returned += 1;
435 return 0;
436}
437
438bool waitForSleepBlock(Thread* thread, InterruptedSleepContext& context) {
439 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
440 while (Time::getTicks() < deadline) {
441 Thread::WaitDebugInfo info = {};
442 uintptr_t debugAddress = 0;
443 if (context.ready && thread->getWaitDebugInfo(info) && info.queue && info.queued &&
444 thread->getDebugState(debugAddress) == Thread::EventWait) {
445 return true;
446 }
447 if (context.returned) {
448 return false;
449 }
451 }
452 return false;
453}
454
455bool waitForSleepReturn(InterruptedSleepContext& context) {
456 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
457 while (Time::getTicks() < deadline) {
458 if (context.returned) {
459 return true;
460 }
462 }
463 return false;
464}
465
466bool interruptedSleep(Process* kernelProcess, const struct timespec& request,
467 RemainderTarget target, bool expectSaturation, SleepCall call = NanosleepCall,
468 clockid_t clockId = CLOCK_MONOTONIC) {
469 PosixProcess* process = new PosixProcess(kernelProcess);
470 process->setSubsystem(new PosixSubsystem);
471 InterruptedSleepContext context(request, target, call, clockId);
472 Thread* worker =
473 new Thread(process, exerciseInterruptedSleep, &context, nullptr, false, true, true);
474 if (expectSaturation) {
475 worker->setName("hosted saturated nanosleep worker");
476 } else {
477 worker->setName("hosted interrupted nanosleep worker");
478 }
479 process->publish();
480
481 g_SleepSignalCalls = 0;
482 const bool started = worker->start();
483 const bool blocked = started && waitForSleepBlock(worker, context);
484 SignalEvent* signal = new SignalEvent(reinterpret_cast<uintptr_t>(&sleepSignalHandler),
485 SleepSignal, ~0UL, 0, true, true);
486 const bool signalQueued = started && worker->sendEvent(signal);
487 if (!signalQueued) {
488 delete signal;
489 }
490
491 const bool returned = started && waitForSleepReturn(context);
492 if (started && !returned) {
493 worker->setUnwindState(Thread::TerminateThread);
494 }
495 const bool joined = started && worker->joinForCompletion();
496 if (!started) {
497 delete worker;
498 }
499
500 bool validResult = false;
501 if (call == AbsoluteClockNanosleepCall) {
502 validResult =
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) {
508 validResult =
509 context.result == -1 && context.error == Error::BadAddress && !context.copiedRemainder;
510 } else {
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));
517 }
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;
524 delete process;
525 return passed;
526}
527} // namespace
528
529bool runHostedSleepClockSyscallRegressions(Process* kernelProcess) {
530 PosixProcess* process = new PosixProcess(kernelProcess);
531 process->setSubsystem(new PosixSubsystem);
532 ValidationContext validation;
533 Thread* worker =
534 new Thread(process, exerciseSleepClockValidation, &validation, nullptr, false, true, true);
535 worker->setName("hosted sleep and clock syscall validation worker");
536 process->publish();
537
538 const bool started = worker->start();
539 const bool joined = started && worker->joinForCompletion();
540 if (!started) {
541 delete worker;
542 }
543 bool passed = started && joined && validation.returned == 1 && validation.passed;
544 delete process;
545
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);
561
562 if (!passed) {
563 ERROR(
564 "HOSTED-SYSCALL-TEST: FAIL sleep-clock-usercopy: "
565 "Linux timespec validation, saturation, interruption, or usercopy behavior regressed");
566 return false;
567 }
568 NOTICE("HOSTED-SYSCALL-TEST: PASS sleep-clock-usercopy");
569 return true;
570}
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 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 publish()
Definition Process.cc:832
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
@ TerminateThread
Exit only this thread during Process exit.
Definition Thread.h:515
bool getWaitDebugInfo(WaitDebugInfo &info)
Definition Thread.cc:3184
bool hasEvent(Event *pEvent)
Definition Thread.cc:2639
size_t getErrno()
Definition Thread.h:473
void cullEvent(Event *pEvent)
Definition Thread.cc:2193
DebugState getDebugState(uintptr_t &address)
Definition Thread.h:570
Process * getParent() const
Definition Thread.h:338
bool sendEvent(Event *pEvent)
Definition Thread.cc:1158