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 // A Linux time32 request ends at the page boundary; musl's native timespec does not fit.
324 const LinuxKernelTimespec32 oneNanosecond32 = {0, 1};
325 passed &= PosixSubsystem::copyToUser(pageEdge, &oneNanosecond32, sizeof(oneNanosecond32));
326 thread->setErrno(PreservedErrno);
327 passed &= posix_nanosleep(pageEdge, nullptr, true) == 0 && thread->getErrno() == PreservedErrno;
328
329 MemoryMapManager::instance().remove(address, pageSize);
330 MemoryMapManager::instance().remove(address + (pageSize * 2), pageSize);
331 process->freeUserRange(Process::UserRegion::Normal, address, mappingLength);
332
333 context->passed = passed;
334 context->returned += 1;
335 return passed ? 0 : 1;
336}
337
338enum RemainderTarget { ValidRemainder, InvalidRemainder };
339enum SleepCall { NanosleepCall, RelativeClockNanosleepCall, AbsoluteClockNanosleepCall };
340
341struct InterruptedSleepContext {
342 InterruptedSleepContext(struct timespec request, RemainderTarget target,
343 SleepCall call = NanosleepCall, clockid_t clockId = CLOCK_MONOTONIC)
344 : request(request),
345 target(target),
346 call(call),
347 clockId(clockId),
348 ready(0),
349 returned(0),
350 mapped(0),
351 copiedRemainder(0),
352 result(0),
353 error(0),
354 remaining{} {}
355
356 struct timespec request;
357 RemainderTarget target;
358 SleepCall call;
359 clockid_t clockId;
360 Atomic<size_t> ready;
361 Atomic<size_t> returned;
362 Atomic<size_t> mapped;
363 Atomic<size_t> copiedRemainder;
364 int result;
365 size_t error;
366 struct timespec remaining;
367};
368
369int exerciseInterruptedSleep(void* parameter) {
370 InterruptedSleepContext* context = reinterpret_cast<InterruptedSleepContext*>(parameter);
371 Thread* thread = Processor::information().getCurrentThread();
372 PosixProcess* process = static_cast<PosixProcess*>(thread->getParent());
373
374 const size_t pageSize = PhysicalMemoryManager::getPageSize();
375 uintptr_t address = 0;
376 const bool allocated = process->allocateUserRange(Process::UserRegion::Normal, pageSize, address);
377 uintptr_t mappedAddress = address;
378 MemoryMappedObject* mapping =
380 mappedAddress, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write)
381 : nullptr;
382 if (!mapping || mappedAddress != address) {
383 if (mapping) {
384 MemoryMapManager::instance().remove(mappedAddress, pageSize);
385 }
386 if (allocated) {
387 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
388 }
389 context->returned += 1;
390 return 1;
391 }
392
393 struct timespec* input = reinterpret_cast<struct timespec*>(address + 64);
394 struct timespec* output = reinterpret_cast<struct timespec*>(address + 256);
395 const uintptr_t kernelStart = Processor::information().getVirtualAddressSpace().getKernelStart();
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()) +
405 duration;
406 syscallRequest.tv_sec = static_cast<time_t>(deadline / Time::Multiplier::Second);
407 syscallRequest.tv_nsec = static_cast<long>(deadline % Time::Multiplier::Second);
408 }
409
410 const struct timespec untouchedRemainder = {UntouchedRemainderSeconds,
411 UntouchedRemainderNanoseconds};
412 if (!PosixSubsystem::copyToUser(input, &syscallRequest, sizeof(syscallRequest)) ||
413 (context->target == ValidRemainder &&
414 !PosixSubsystem::copyToUser(output, &untouchedRemainder, sizeof(untouchedRemainder)))) {
415 MemoryMapManager::instance().remove(address, pageSize);
416 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
417 context->returned += 1;
418 return 1;
419 }
420
421 context->mapped += 1;
422 context->ready += 1;
423 thread->setErrno(PreservedErrno);
424 if (context->call == NanosleepCall) {
425 context->result = posix_nanosleep(input, remainder);
426 } else {
427 context->result = posix_clock_nanosleep(
428 context->clockId, context->call == AbsoluteClockNanosleepCall ? TIMER_ABSTIME : 0,
429 reinterpret_cast<LinuxKernelTimespec*>(input),
430 reinterpret_cast<LinuxKernelTimespec*>(remainder));
431 }
432 context->error = thread->getErrno();
433 if (context->target == ValidRemainder &&
434 PosixSubsystem::copyFromUser(&context->remaining, output, sizeof(context->remaining))) {
435 context->copiedRemainder += 1;
436 }
437
438 MemoryMapManager::instance().remove(address, pageSize);
439 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
440 context->returned += 1;
441 return 0;
442}
443
444bool waitForSleepBlock(Thread* thread, InterruptedSleepContext& context) {
445 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
446 while (Time::getTicks() < deadline) {
447 Thread::WaitDebugInfo info = {};
448 uintptr_t debugAddress = 0;
449 if (context.ready && thread->getWaitDebugInfo(info) && info.queue && info.queued &&
450 thread->getDebugState(debugAddress) == Thread::EventWait) {
451 return true;
452 }
453 if (context.returned) {
454 return false;
455 }
457 }
458 return false;
459}
460
461bool waitForSleepReturn(InterruptedSleepContext& context) {
462 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
463 while (Time::getTicks() < deadline) {
464 if (context.returned) {
465 return true;
466 }
468 }
469 return false;
470}
471
472bool interruptedSleep(Process* kernelProcess, const struct timespec& request,
473 RemainderTarget target, bool expectSaturation, SleepCall call = NanosleepCall,
474 clockid_t clockId = CLOCK_MONOTONIC) {
475 PosixProcess* process = new PosixProcess(kernelProcess);
476 process->setSubsystem(new PosixSubsystem);
477 InterruptedSleepContext context(request, target, call, clockId);
478 Thread* worker =
479 new Thread(process, exerciseInterruptedSleep, &context, nullptr, false, true, true);
480 if (expectSaturation) {
481 worker->setName("hosted saturated nanosleep worker");
482 } else {
483 worker->setName("hosted interrupted nanosleep worker");
484 }
485 process->publish();
486
487 g_SleepSignalCalls = 0;
488 const bool started = worker->start();
489 const bool blocked = started && waitForSleepBlock(worker, context);
490 SignalEvent* signal = new SignalEvent(reinterpret_cast<uintptr_t>(&sleepSignalHandler),
491 SleepSignal, ~0UL, 0, true, true);
492 const bool signalQueued = started && worker->sendEvent(signal);
493 if (!signalQueued) {
494 delete signal;
495 }
496
497 const bool returned = started && waitForSleepReturn(context);
498 if (started && !returned) {
499 worker->setUnwindState(Thread::TerminateThread);
500 }
501 const bool joined = started && worker->joinForCompletion();
502 if (!started) {
503 delete worker;
504 }
505
506 bool validResult = false;
507 if (call == AbsoluteClockNanosleepCall) {
508 validResult =
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) {
514 validResult =
515 context.result == -1 && context.error == Error::BadAddress && !context.copiedRemainder;
516 } else {
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));
523 }
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;
530 delete process;
531 return passed;
532}
533} // namespace
534
535bool runHostedSleepClockSyscallRegressions(Process* kernelProcess) {
536 PosixProcess* process = new PosixProcess(kernelProcess);
537 process->setSubsystem(new PosixSubsystem);
538 ValidationContext validation;
539 Thread* worker =
540 new Thread(process, exerciseSleepClockValidation, &validation, nullptr, false, true, true);
541 worker->setName("hosted sleep and clock syscall validation worker");
542 process->publish();
543
544 const bool started = worker->start();
545 const bool joined = started && worker->joinForCompletion();
546 if (!started) {
547 delete worker;
548 }
549 bool passed = started && joined && validation.returned == 1 && validation.passed;
550 delete process;
551
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);
567
568 if (!passed) {
569 ERROR(
570 "HOSTED-SYSCALL-TEST: FAIL sleep-clock-usercopy: "
571 "Linux timespec validation, saturation, interruption, or usercopy behavior regressed");
572 return false;
573 }
574 NOTICE("HOSTED-SYSCALL-TEST: PASS sleep-clock-usercopy");
575 return true;
576}
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:944
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
void setErrno(size_t err)
Definition Thread.h:482
@ TerminateThread
Exit only this thread during Process exit.
Definition Thread.h:519
bool getWaitDebugInfo(WaitDebugInfo &info)
Definition Thread.cc:3163
bool hasEvent(Event *pEvent)
Definition Thread.cc:2618
size_t getErrno()
Definition Thread.h:477
void cullEvent(Event *pEvent)
Definition Thread.cc:2172
DebugState getDebugState(uintptr_t &address)
Definition Thread.h:574
Process * getParent() const
Definition Thread.h:340
bool sendEvent(Event *pEvent)
Definition Thread.cc:1115