The Pedigree Project 0.1
signal-interruption-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/process/ConditionVariable.h"
11#include "pedigree/kernel/process/Mutex.h"
12#include "pedigree/kernel/process/PerProcessorScheduler.h"
13#include "pedigree/kernel/process/Scheduler.h"
14#include "pedigree/kernel/process/Semaphore.h"
15#include "pedigree/kernel/process/SignalEvent.h"
16#include "pedigree/kernel/process/Thread.h"
17#include "pedigree/kernel/process/Uninterruptible.h"
18#include "pedigree/kernel/processor/Processor.h"
19#include "pedigree/kernel/processor/ProcessorInformation.h"
20#include "pedigree/kernel/processor/VirtualAddressSpace.h"
21#include "pedigree/kernel/processor/state.h"
22#include "pedigree/kernel/time/Time.h"
23#include "pedigree/kernel/utilities/Buffer.h"
24#include "pedigree/kernel/utilities/RingBuffer.h"
25
26#if !defined(PEDIGREE_HOSTED_CORE_SMOKE)
27#include <signal.h>
28
29#include "modules/subsys/posix/PosixProcess.h"
30#include "modules/subsys/posix/PosixSubsystem.h"
31#include "modules/subsys/posix/signal-syscalls.h"
32#endif
33
34namespace {
35constexpr size_t HostedSignalNumber = 10;
36
37Atomic<size_t> g_SignalHandlerCalls(0);
38Atomic<size_t> g_MonitorEventDestructions(0);
39Atomic<size_t> g_NestedWaitHandlerCalls(0);
40Atomic<size_t> g_NestedWaitHandlerLevel(0);
41Atomic<size_t> g_NestedWaitReturned(0);
42Atomic<size_t> g_NestedSignalHandlerCalls(0);
43Atomic<size_t> g_NestedSignalHandlerLevel(0);
44Atomic<size_t> g_DefaultActionHandlerCalls(0);
45Atomic<size_t> g_ExactUserReturnCalls(0);
46Atomic<size_t> g_ExactUserReturnSawInterrupts(0);
47
48void hostedSignalHandler(size_t) {
49 g_SignalHandlerCalls += 1;
50}
51
52void hostedNestedSignalHandler(size_t) {
53 Thread* thread = Processor::information().getCurrentThread();
54 g_NestedSignalHandlerCalls += 1;
55 g_NestedSignalHandlerLevel = thread ? thread->getStateLevel() : 0;
56}
57
58void hostedNestedWaitHandler(size_t) {
59 Thread* thread = Processor::information().getCurrentThread();
60 if (!thread) {
61 return;
62 }
63
64 const size_t stateLevel = thread->getStateLevel();
65 g_NestedWaitHandlerCalls += 1;
66 g_NestedWaitHandlerLevel = stateLevel;
67 thread->waitForEvent();
68 if (thread->getStateLevel() == stateLevel) {
69 g_NestedWaitReturned += 1;
70 }
71}
72
73void hostedDefaultActionHandler(size_t) {
74 g_DefaultActionHandlerCalls += 1;
75}
76
77class HostedNestedWaitEvent : public Event {
78 public:
79 HostedNestedWaitEvent() : Event(reinterpret_cast<uintptr_t>(&hostedNestedWaitHandler), false) {}
80
81 size_t serialize(uint8_t*) override {
82 return 0;
83 }
84
85 size_t getNumber() override {
86 return 0x4e535457;
87 }
88};
89
90class HostedMonitorEvent : public Event {
91 public:
92 HostedMonitorEvent() : Event(reinterpret_cast<uintptr_t>(&hostedSignalHandler), false) {}
93
94 ~HostedMonitorEvent() override {
95 g_MonitorEventDestructions += 1;
96 }
97
98 size_t serialize(uint8_t*) override {
99 return 0;
100 }
101
102 size_t getNumber() override {
103 return 0x4d4f4e49;
104 }
105};
106
107class SignalNumberCollisionEvent : public Event {
108 public:
109 SignalNumberCollisionEvent()
110 : Event(reinterpret_cast<uintptr_t>(&hostedSignalHandler), false, MAX_NESTED_EVENTS) {}
111
112 size_t serialize(uint8_t*) override {
113 return 0;
114 }
115
116 size_t getNumber() override {
117 return HostedSignalNumber;
118 }
119};
120
121class HostedDeferredUserReturnSignalEvent : public SignalEvent {
122 public:
124
125 HostedDeferredUserReturnSignalEvent()
126 : SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber, ~0UL, 0,
127 true, false, Event::HandlerPrivilege::User) {}
128
129 bool requiresExactUserReturnState() const override {
130 return true;
131 }
132
133 UserReturnDelivery deliverAtUserReturn(SyscallState&) override {
134 g_ExactUserReturnCalls += 1;
136 g_ExactUserReturnSawInterrupts += 1;
137 }
138 return UserReturnDelivery::Delivered;
139 }
140};
141
142bool check(bool condition, const char* detail) {
143 if (condition) {
144 return true;
145 }
146
147 ERROR("HOSTED-WAIT-TEST: FAIL signal-interruption: " << detail);
148 return false;
149}
150
151bool waitUntilQueued(Thread* thread, size_t debugState) {
152 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
153 while (Time::getTicks() < deadline) {
154 Thread::WaitDebugInfo info = {};
155 uintptr_t debugAddress = 0;
156 if (thread->getWaitDebugInfo(info) && info.queue && info.queued &&
157 thread->getDebugState(debugAddress) == debugState) {
158 return true;
159 }
161 }
162 return false;
163}
164
165bool eventHandlerPrivilege() {
166 constexpr const char* Test = "event-handler-privilege";
167 SignalEvent kernelEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber);
168 SignalEvent userEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber, ~0UL,
169 0, true, false, Event::HandlerPrivilege::User);
170 Event* delivery = userEvent.cloneForDelivery();
171 SignalEvent alternateEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber,
172 ~0UL, 0, true, false, Event::HandlerPrivilege::User,
173 SignalEvent::DeliveryDisposition::CaughtHandler, true);
174 Event* alternateDelivery = alternateEvent.cloneForDelivery();
175
176 const bool passed =
177 check(kernelEvent.getHandlerPrivilege() == Event::HandlerPrivilege::Kernel,
178 "the compatible Event constructor did not default to kernel privilege") &&
179 check(kernelEvent.isValidHandlerMapping(VirtualAddressSpace::KernelMode),
180 "a kernel event rejected a kernel mapping") &&
181 check(!kernelEvent.isValidHandlerMapping(VirtualAddressSpace::Execute),
182 "a kernel event accepted a userspace mapping") &&
183 check(userEvent.getHandlerPrivilege() == Event::HandlerPrivilege::User,
184 "a user event lost its explicit privilege") &&
185 check(userEvent.isValidHandlerMapping(VirtualAddressSpace::Execute),
186 "a user event rejected an executable userspace mapping") &&
187 check(!userEvent.isValidHandlerMapping(0),
188 "a user event accepted a non-executable mapping") &&
189 check(!userEvent.isValidHandlerMapping(VirtualAddressSpace::KernelMode |
191 "a user event accepted a kernel mapping") &&
192 check(delivery && delivery->getHandlerPrivilege() == Event::HandlerPrivilege::User,
193 "a signal delivery snapshot lost its user privilege") &&
194 check(alternateEvent.prefersAlternateUserStack() && alternateDelivery &&
195 alternateDelivery->prefersAlternateUserStack(),
196 "a signal delivery snapshot lost its alternate-stack preference");
197
198 delete delivery;
199 delete alternateDelivery;
200 if (passed) {
201 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
202 }
203 return passed;
204}
205
206bool pendingSignalRunsAtSyscallReturn(Thread* thread) {
207 constexpr const char* Test = "pending-signal-at-syscall-return";
208 constexpr uint64_t SignalBit = static_cast<uint64_t>(1) << (HostedSignalNumber - 1);
209 const uint64_t originalMask = thread->getSignalMask();
210 const size_t originalLevel = thread->getStateLevel();
211 g_SignalHandlerCalls = 0;
212
213 thread->setSignalMask(originalMask | SignalBit);
214 SignalEvent event(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber);
215 const bool queued = thread->sendEvent(&event);
216
217 SyscallState state = {};
218 const bool terminalWhileBlocked = thread->getScheduler()->serviceUserReturnWork(state);
219 const bool stayedPending = thread->hasEvent(&event) && g_SignalHandlerCalls == 0;
220
221 thread->setSignalMask(originalMask & ~SignalBit);
222 const bool terminalAfterUnblock = thread->getScheduler()->serviceUserReturnWork(state);
223 const bool delivered = !thread->hasEvent(&event) && g_SignalHandlerCalls == 1;
224
225 thread->setSignalMask(originalMask);
226 const bool passed =
227 check(queued && !terminalWhileBlocked && stayedPending && !terminalAfterUnblock &&
228 delivered && thread->getStateLevel() == originalLevel,
229 "a newly unblocked signal did not run at the syscall return boundary");
230 if (passed) {
231 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
232 }
233 return passed;
234}
235
236bool exactUserReturnSignalDefersWithoutContext(Thread* thread) {
237 constexpr const char* Test = "exact-user-return-signal-deferral";
238 const size_t originalLevel = thread->getStateLevel();
239 thread->clearInterruption();
240 g_SignalHandlerCalls = 0;
241 g_ExactUserReturnCalls = 0;
242 g_ExactUserReturnSawInterrupts = 0;
243
244 HostedDeferredUserReturnSignalEvent event;
245 const bool queued = thread->sendEvent(&event);
246 const bool delayed = queued ? Time::delay(5 * Time::Multiplier::Second) : true;
247 const Thread::InterruptionReason reason = thread->getInterruptionReason();
248 const bool stayedPending = thread->hasEvent(&event);
249 thread->clearInterruption();
250
251 SyscallState state = {};
252 const bool interruptsBeforeDelivery = Processor::getInterrupts();
253 const bool terminal = stayedPending ? thread->getScheduler()->serviceUserReturnWork(state) : true;
254 const bool deliveredAtExactBoundary = !thread->hasEvent(&event) && g_ExactUserReturnCalls == 1 &&
255 g_ExactUserReturnSawInterrupts == 1 &&
256 Processor::getInterrupts() == interruptsBeforeDelivery;
257 if (!deliveredAtExactBoundary) {
258 thread->cullEvent(&event);
259 }
260
261 const bool passed =
262 check(queued && !delayed && reason == Thread::InterruptedBySignal && stayedPending &&
263 !terminal && deliveredAtExactBoundary && !g_SignalHandlerCalls &&
264 thread->getStateLevel() == originalLevel,
265 "an exact-context signal ran from a wait boundary or was not delivered IRQ-enabled");
266 if (passed) {
267 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
268 }
269 return passed;
270}
271
272bool signalCullPreservesNumberCollision(Thread* thread) {
273 constexpr const char* Test = "signal-cull-number-collision";
274 constexpr uint64_t SignalBit = static_cast<uint64_t>(1) << (HostedSignalNumber - 1);
275 const uint64_t originalMask = thread->getSignalMask();
276 thread->setSignalMask(originalMask | SignalBit);
277
278 SignalNumberCollisionEvent collision;
279 SignalEvent signal(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber);
280 const bool collisionQueued = thread->sendEvent(&collision);
281 const bool signalQueued = collisionQueued && thread->sendEvent(&signal);
282 thread->cullSignalEvent(HostedSignalNumber);
283 const bool preservedCollision = thread->hasEvent(&collision) && !thread->hasEvent(&signal);
284 thread->cullEvent(&collision);
285 thread->setSignalMask(originalMask);
286
287 const bool passed =
288 check(collisionQueued && signalQueued && preservedCollision,
289 "signal culling removed a non-signal event with the same numeric identifier");
290 if (passed) {
291 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
292 }
293 return passed;
294}
295
296#if !defined(PEDIGREE_HOSTED_CORE_SMOKE)
297Atomic<size_t> g_ContinueHandlerCalls(0);
298Atomic<size_t> g_ContinueHandlerObservedActive(0);
299
300struct DefaultStopContext {
301 DefaultStopContext() : entered(0), returned(0) {}
302
303 Atomic<size_t> entered;
304 Atomic<size_t> returned;
305};
306
307struct StopEpochHookContext {
308 enum Mode {
309 ContinueOnce,
310 ContinueThenFreshStop,
311 };
312
313 StopEpochHookContext(PosixSubsystem* subsystem, Thread* staleTarget, Thread* freshTarget,
314 Mode mode)
315 : subsystem(subsystem),
316 staleTarget(staleTarget),
317 freshTarget(freshTarget),
318 mode(mode),
319 hookCalls(0),
320 failures(0),
321 continuations(0),
322 freshStopQueued(0),
323 freshStarted(0),
324 freshSuspended(0),
325 cancelled(0),
326 hookCompleted(0),
327 epochBefore(0),
328 epochAfter(0) {}
329
330 PosixSubsystem* subsystem;
331 Thread* staleTarget;
332 Thread* freshTarget;
333 Mode mode;
334 Atomic<size_t> hookCalls;
335 Atomic<size_t> failures;
336 Atomic<size_t> continuations;
337 Atomic<size_t> freshStopQueued;
338 Atomic<size_t> freshStarted;
339 Atomic<size_t> freshSuspended;
340 Atomic<size_t> cancelled;
341 Atomic<size_t> hookCompleted;
342 Atomic<size_t> epochBefore;
343 Atomic<size_t> epochAfter;
344};
345
346StopEpochHookContext* g_StopEpochHookContext = nullptr;
347
348void continueAfterStopDequeue(Thread::StateTransitionWindow window, Thread* thread, size_t,
349 size_t) {
350 StopEpochHookContext* context = __atomic_load_n(&g_StopEpochHookContext, __ATOMIC_ACQUIRE);
351 if (!context || window != Thread::StatePushBeforePublish || thread != context->staleTarget) {
352 return;
353 }
354
355 Thread::setStateTransitionHook(nullptr);
356 context->hookCalls += 1;
357 Process* process = thread->getParent();
358 if (!context->cancelled) {
359 context->epochBefore = process->getContinuationEpoch();
360
361 const size_t continuationCount =
362 context->mode == StopEpochHookContext::ContinueThenFreshStop ? 2 : 1;
363 for (size_t i = 0; i < continuationCount && !context->cancelled; ++i) {
364 if (context->subsystem->queueSignalDelivery(thread, SIGCONT) ==
365 PosixSubsystem::SignalDeliveryResult::Ignored) {
366 context->continuations += 1;
367 } else {
368 context->failures += 1;
369 }
370 }
371 context->epochAfter = process->getContinuationEpoch();
372 }
373
374 if (context->freshTarget && !context->cancelled) {
375 if (context->subsystem->queueSignalDelivery(context->freshTarget, SIGSTOP) ==
376 PosixSubsystem::SignalDeliveryResult::Queued) {
377 context->freshStopQueued += 1;
378 if (!context->cancelled && context->freshTarget->start()) {
379 context->freshStarted += 1;
380 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
381 while (!context->cancelled && !process->isSuspended() && Time::getTicks() < deadline) {
383 }
384 if (process->isSuspended()) {
385 context->freshSuspended += 1;
386 } else if (!context->cancelled) {
387 context->failures += 1;
388 }
389 } else if (!context->cancelled) {
390 context->failures += 1;
391 }
392 } else {
393 context->failures += 1;
394 }
395 }
396 context->hookCompleted += 1;
397}
398
399void hostedContinueHandler(size_t) {
400 Thread* current = Processor::information().getCurrentThread();
401 if (current && current->getParent()->getState() == Process::Active) {
402 g_ContinueHandlerObservedActive += 1;
403 }
404 g_ContinueHandlerCalls += 1;
405}
406
407void installSignalDisposition(PosixSubsystem& subsystem, size_t signal, int type,
408 void (*handlerAddress)(size_t) = &hostedSignalHandler) {
410 handler->type = type;
411 handler->pEvent = new SignalEvent(reinterpret_cast<uintptr_t>(handlerAddress), signal);
412 subsystem.setSignalHandler(signal, handler);
413}
414
415int dormantSignalThread(void*) {
416 return 0;
417}
418
419int waitForDefaultStop(void* parameter) {
420 DefaultStopContext* context = reinterpret_cast<DefaultStopContext*>(parameter);
421 context->entered += 1;
422 Processor::information().getCurrentThread()->waitForEvent();
423 context->returned += 1;
424 return 0;
425}
426
427struct ExecSignalResetContext {
428 ExecSignalResetContext() : entered(0), returned(0) {}
429
430 Atomic<size_t> entered;
431 Atomic<size_t> returned;
432};
433
434int deliverExecResetSignal(void* parameter) {
435 ExecSignalResetContext* context = reinterpret_cast<ExecSignalResetContext*>(parameter);
436 Thread* current = Processor::information().getCurrentThread();
437 context->entered += 1;
438 current->setSignalMask(0);
439 current->getScheduler()->checkEventState(0);
440 context->returned += 1;
441 return 0;
442}
443
444struct IgnoredContinueContext {
445 IgnoredContinueContext(Process* process, bool blockSignal)
446 : process(process), blockSignal(blockSignal), entered(0), returned(0) {}
447
448 Process* process;
449 bool blockSignal;
450 Atomic<size_t> entered;
451 Atomic<size_t> returned;
452};
453
454struct IgnoredSignalWaitContext {
455 explicit IgnoredSignalWaitContext(size_t blockedSignal = 0)
456 : gate(0),
457 blockedSignal(blockedSignal),
458 entered(0),
459 returned(0),
460 acquired(0),
461 error(Semaphore::NoError),
462 interruption(Thread::NotInterrupted) {}
463
464 Semaphore gate;
465 size_t blockedSignal;
466 Atomic<size_t> entered;
467 Atomic<size_t> returned;
468 Atomic<size_t> acquired;
469 Atomic<size_t> error;
470 Atomic<size_t> interruption;
471};
472
473int waitThroughIgnoredSignal(void* parameter) {
474 IgnoredSignalWaitContext* context = reinterpret_cast<IgnoredSignalWaitContext*>(parameter);
475 Thread* current = Processor::information().getCurrentThread();
476 const uint64_t originalMask = current->getSignalMask();
477 if (context->blockedSignal) {
478 current->setSignalMask(originalMask |
479 (static_cast<uint64_t>(1) << (context->blockedSignal - 1)));
480 }
481 context->entered += 1;
482 Semaphore::SemaphoreError error = Semaphore::NoError;
483 context->acquired = context->gate.acquireWithError(1, 0, 0, error) ? 1 : 0;
484 context->error = static_cast<size_t>(error);
485 if (context->blockedSignal) {
486 current->setSignalMask(originalMask);
487 current->getScheduler()->checkEventState(0);
488 }
489 context->interruption = static_cast<size_t>(current->getInterruptionReason());
490 current->clearInterruption();
491 context->returned += 1;
492 return 0;
493}
494
495bool ignoredSignalDoesNotWakeWait(PosixProcess* process, PosixSubsystem* subsystem, size_t signal,
496 int type) {
497 installSignalDisposition(*subsystem, signal, type);
499 const bool queryPreserved =
500 subsystem->getSignalDisposition(signal, disposition) && disposition.type == type;
501 IgnoredSignalWaitContext context;
502 Thread* target =
503 new Thread(process, waitThroughIgnoredSignal, &context, nullptr, false, true, true);
504 target->setName("hosted ignored signal waiter");
505 const bool started = target->start();
506 const bool queued = started && waitUntilQueued(target, Thread::SemWait);
507
508 if (queued) {
509 subsystem->sendSignal(target, static_cast<int>(signal), false);
510 }
511 for (size_t attempt = 0; attempt < 32 && !context.returned; ++attempt) {
513 }
514 Thread::WaitDebugInfo wait = {};
515 const bool stayedQueued = queued && !context.returned && target->getWaitDebugInfo(wait) &&
516 wait.queued && !target->hasEvents();
517
518 context.gate.release();
519 const bool joined = started && target->joinForCompletion();
520 if (!started) {
521 delete target;
522 }
523 return queryPreserved && started && queued && stayedQueued && joined && context.entered == 1 &&
524 context.returned == 1 && context.acquired == 1 && context.error == Semaphore::NoError &&
525 context.interruption == Thread::NotInterrupted;
526}
527
528bool ignoredSignalDoesNotInterruptWait(Process* kernelProcess) {
529 constexpr const char* Test = "ignored-signal-does-not-interrupt";
530 PosixProcess* process = new PosixProcess(kernelProcess);
531 PosixSubsystem* subsystem = new PosixSubsystem;
532 process->setSubsystem(subsystem);
533 process->publish();
534
535 g_SignalHandlerCalls = 0;
536 const bool explicitIgnore = ignoredSignalDoesNotWakeWait(process, subsystem, SIGUSR1, 2);
537 const bool defaultIgnore = ignoredSignalDoesNotWakeWait(process, subsystem, SIGCHLD, 1);
538 const bool passed = check(explicitIgnore && defaultIgnore && g_SignalHandlerCalls == 0,
539 "an explicit or default ignored signal woke an interruptible wait");
540 delete process;
541
542 if (passed) {
543 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
544 }
545 return passed;
546}
547
548bool pendingSignalDiscarded(PosixProcess* process, PosixSubsystem* subsystem, size_t signal,
549 int ignoredType) {
550 installSignalDisposition(*subsystem, signal, 0);
551 IgnoredSignalWaitContext context(signal);
552 Thread* target =
553 new Thread(process, waitThroughIgnoredSignal, &context, nullptr, false, true, true);
554 target->setName("hosted pending signal discard target");
555 const bool started = target->start();
556 const bool waiting = started && waitUntilQueued(target, Thread::SemWait);
557
558 const PosixSubsystem::SignalDeliveryResult queued =
559 waiting ? subsystem->queueSignalDelivery(target, signal)
560 : PosixSubsystem::SignalDeliveryResult::Rejected;
561 const bool observedPending =
562 queued == PosixSubsystem::SignalDeliveryResult::Queued && target->hasEvent(signal);
563 installSignalDisposition(*subsystem, signal, ignoredType);
564 const bool discarded = observedPending && !target->hasEvent(signal);
565
566 context.gate.release();
567 const bool joined = started && target->joinForCompletion();
568 if (!started) {
569 delete target;
570 }
571 return waiting && observedPending && discarded && joined && context.entered == 1 &&
572 context.returned == 1 && context.acquired == 1 && context.error == Semaphore::NoError &&
573 context.interruption == Thread::NotInterrupted;
574}
575
576bool ignoredDispositionDiscardsPendingSignals(Process* kernelProcess) {
577 constexpr const char* Test = "ignored-disposition-discards-pending";
578 PosixProcess* process = new PosixProcess(kernelProcess);
579 PosixSubsystem* subsystem = new PosixSubsystem;
580 process->setSubsystem(subsystem);
581 process->publish();
582
583 g_SignalHandlerCalls = 0;
584 const bool explicitIgnore = pendingSignalDiscarded(process, subsystem, SIGUSR1, 2);
585 const bool defaultIgnore = pendingSignalDiscarded(process, subsystem, SIGCHLD, 1);
586 const bool passed =
587 check(explicitIgnore && defaultIgnore && g_SignalHandlerCalls == 0,
588 "a pending caught signal survived transition to an ignored disposition");
589 delete process;
590
591 if (passed) {
592 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
593 }
594 return passed;
595}
596
597bool execSignalResetRebindsPending(Process* kernelProcess) {
598 constexpr const char* Test = "exec-signal-reset-rebinds-pending";
599 PosixProcess* process = new PosixProcess(kernelProcess);
600 PosixSubsystem* subsystem = new PosixSubsystem;
601 process->setSubsystem(subsystem);
602 process->publish();
603
605 caught->sigMask = static_cast<uint64_t>(1) << (SIGUSR1 - 1);
606 caught->flags = SA_RESTART;
607 caught->restorer = 0x12345678;
608 caught->type = 0;
609 caught->pEvent = new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), SIGCHLD);
610 subsystem->setSignalHandler(SIGCHLD, caught);
611
613 ignored->sigMask = static_cast<uint64_t>(1) << (SIGCHLD - 1);
614 ignored->flags = SA_RESTART;
615 ignored->restorer = 0x87654321;
616 ignored->type = 2;
617 ignored->pEvent = new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), SIGUSR2);
618 subsystem->setSignalHandler(SIGUSR2, ignored);
619
620 ExecSignalResetContext context;
621 Thread* target =
622 new Thread(process, deliverExecResetSignal, &context, nullptr, false, true, true);
623 target->setName("hosted exec signal reset target");
624 target->setSignalMask(static_cast<uint64_t>(1) << (SIGCHLD - 1));
625 Thread::AlternateSignalStack& alternate = target->getAlternateSignalStack();
626 alternate.base = 0x100000;
627 alternate.size = 0x4000;
628 alternate.enabled = true;
629 alternate.inUse = true;
630
631 g_SignalHandlerCalls = 0;
632 const PosixSubsystem::SignalDeliveryResult queued =
633 subsystem->queueSignalDelivery(target, SIGCHLD);
634 const bool initiallyPending =
635 queued == PosixSubsystem::SignalDeliveryResult::Queued && target->hasSignalEvent(SIGCHLD);
636
637 pedigree_reset_signals_for_exec(target);
638
641 const bool caughtReset = subsystem->getSignalDisposition(SIGCHLD, resetCaught) &&
642 resetCaught.type == 1 && !resetCaught.signalMask && !resetCaught.flags &&
643 !resetCaught.restorer &&
644 resetCaught.handler != reinterpret_cast<uintptr_t>(&hostedSignalHandler);
645 const bool ignoreRetained =
646 subsystem->getSignalDisposition(SIGUSR2, resetIgnored) && resetIgnored.type == 2 &&
647 !resetIgnored.signalMask && !resetIgnored.flags && !resetIgnored.restorer &&
648 resetIgnored.handler != reinterpret_cast<uintptr_t>(&hostedSignalHandler);
649 const bool pendingRebound = target->hasSignalEvent(SIGCHLD);
650 const bool alternateReset =
651 !alternate.base && !alternate.size && !alternate.enabled && !alternate.inUse;
652
653 const bool started = target->start();
654 const bool joined = started && target->joinForCompletion();
655 if (!started) {
656 target->cullSignalEvent(SIGCHLD);
657 delete target;
658 }
659
660 const bool passed = check(initiallyPending && caughtReset && ignoreRetained && pendingRebound &&
661 alternateReset && started && joined && context.entered == 1 &&
662 context.returned == 1 && g_SignalHandlerCalls == 0,
663 "exec discarded a pending signal, retained old handler metadata, or "
664 "left the alternate stack enabled");
665 delete process;
666
667 if (passed) {
668 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
669 }
670 return passed;
671}
672
673bool takeJobControlTransition(Process* process, Process::ChildTransition& transition) {
674 Process* parent = process->getParent();
675 if (!parent) {
676 return false;
677 }
678
679 auto guard = parent->acquireChildStateWait();
680 return process->takePendingChildTransition(true, true, transition);
681}
682
683bool defaultStopControl(Process* kernelProcess) {
684 constexpr const char* Test = "default-stop-control";
685 PosixProcess* process = new PosixProcess(kernelProcess);
686 PosixSubsystem* subsystem = new PosixSubsystem;
687 process->setSubsystem(subsystem);
688 process->publish();
689
690 DefaultStopContext context;
691 Thread* target = new Thread(process, waitForDefaultStop, &context, nullptr, false, true, true);
692 target->setName("hosted default stop control");
693 pedigree_reset_signals_for_exec(target);
694
695 const bool queued = subsystem->queueSignalDelivery(target, SIGTSTP) ==
696 PosixSubsystem::SignalDeliveryResult::Queued;
697 const bool started = queued && target->start();
698 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
699 while (started && !process->isSuspended() && !context.returned && Time::getTicks() < deadline) {
701 }
702
703 const bool suspended = process->isSuspended();
704 Process::ChildTransition stoppedTransition;
705 const bool stoppedReported = suspended && takeJobControlTransition(process, stoppedTransition) &&
706 stoppedTransition.kind == Process::ChildTransitionKind::Stopped &&
707 stoppedTransition.stopSignal == SIGTSTP;
708
709 PosixSubsystem::SignalDeliveryResult continueResult =
710 PosixSubsystem::SignalDeliveryResult::Unavailable;
711 if (suspended) {
712 continueResult = subsystem->queueSignalDelivery(target, SIGCONT);
713 if (process->isSuspended()) {
714 process->resume();
715 }
716 } else if (started && !context.returned) {
718 }
719
720 const bool joined = started && target->joinForCompletion();
721 if (!started) {
722 target->cullSignalEvent(SIGTSTP);
723 delete target;
724 }
725
726 Process::ChildTransition continuedTransition;
727 const bool continuedReported =
728 takeJobControlTransition(process, continuedTransition) &&
729 continuedTransition.kind == Process::ChildTransitionKind::Continued &&
730 !continuedTransition.stopSignal;
731 const bool passed =
732 check(queued && started && suspended && stoppedReported &&
733 continueResult == PosixSubsystem::SignalDeliveryResult::Queued && joined &&
734 context.entered == 1 && context.returned == 1 && continuedReported,
735 "a fresh default stop did not suspend, preserve its signal number, or continue");
736 delete process;
737
738 if (passed) {
739 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
740 }
741 return passed;
742}
743
744bool staleDefaultStopRejectedAfterContinue(Process* kernelProcess) {
745 constexpr const char* Test = "stale-default-stop-after-continue";
746 PosixProcess* process = new PosixProcess(kernelProcess);
747 PosixSubsystem* subsystem = new PosixSubsystem;
748 process->setSubsystem(subsystem);
749 process->publish();
750
751 DefaultStopContext context;
752 Thread* target = new Thread(process, waitForDefaultStop, &context, nullptr, false, true, true);
753 target->setName("hosted stale default stop target");
754 pedigree_reset_signals_for_exec(target);
755 installSignalDisposition(*subsystem, SIGCONT, 2);
756
757 const bool queued = subsystem->queueSignalDelivery(target, SIGSTOP) ==
758 PosixSubsystem::SignalDeliveryResult::Queued;
759 StopEpochHookContext hookContext(subsystem, target, nullptr, StopEpochHookContext::ContinueOnce);
760 __atomic_store_n(&g_StopEpochHookContext, &hookContext, __ATOMIC_RELEASE);
761 Thread::setStateTransitionHook(continueAfterStopDequeue);
762 const bool started = queued && target->start();
763
764 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
765 while (started && !context.returned && !process->isSuspended() && Time::getTicks() < deadline) {
767 }
768 Thread::setStateTransitionHook(nullptr);
769 __atomic_store_n(&g_StopEpochHookContext, static_cast<StopEpochHookContext*>(nullptr),
770 __ATOMIC_RELEASE);
771
772 Process::ChildTransition unexpectedTransition;
773 const bool publishedStop = takeJobControlTransition(process, unexpectedTransition);
774 const bool returnedWithoutStop = context.returned == 1 && process->getState() == Process::Active;
775 if (process->isSuspended()) {
776 process->resume();
777 } else if (started && !context.returned) {
779 }
780
781 const bool joined = started && target->joinForCompletion();
782 if (!started) {
783 target->cullSignalEvent(SIGSTOP);
784 delete target;
785 }
786
787 const bool passed =
788 check(queued && started && hookContext.hookCalls == 1 && !hookContext.failures &&
789 hookContext.hookCompleted == 1 && hookContext.continuations == 1 &&
790 hookContext.epochAfter == (hookContext.epochBefore + 1) && returnedWithoutStop &&
791 !publishedStop && joined && context.entered == 1,
792 "a dequeued default stop survived a later SIGCONT generation");
793 delete process;
794
795 if (passed) {
796 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
797 }
798 return passed;
799}
800
801bool staleDefaultStopRejectedAcrossAba(Process* kernelProcess) {
802 constexpr const char* Test = "stale-default-stop-aba";
803 PosixProcess* process = new PosixProcess(kernelProcess);
804 PosixSubsystem* subsystem = new PosixSubsystem;
805 process->setSubsystem(subsystem);
806 process->publish();
807
808 DefaultStopContext staleContext;
809 Thread* staleTarget =
810 new Thread(process, waitForDefaultStop, &staleContext, nullptr, false, true, true);
811 staleTarget->setName("hosted ABA stale stop target");
812 pedigree_reset_signals_for_exec(staleTarget);
813 installSignalDisposition(*subsystem, SIGCONT, 2);
814
815 DefaultStopContext freshContext;
816 Thread* freshTarget =
817 new Thread(process, waitForDefaultStop, &freshContext, nullptr, false, true, true);
818 freshTarget->setName("hosted ABA fresh stop target");
819
820 const bool staleQueued = subsystem->queueSignalDelivery(staleTarget, SIGSTOP) ==
821 PosixSubsystem::SignalDeliveryResult::Queued;
822 StopEpochHookContext hookContext(subsystem, staleTarget, freshTarget,
823 StopEpochHookContext::ContinueThenFreshStop);
824 __atomic_store_n(&g_StopEpochHookContext, &hookContext, __ATOMIC_RELEASE);
825 Thread::setStateTransitionHook(continueAfterStopDequeue);
826 const bool staleStarted = staleQueued && staleTarget->start();
827
828 const Time::Timestamp hookDeadline = Time::getTicks() + (1 * Time::Multiplier::Second);
829 while (staleStarted && !hookContext.hookCompleted && Time::getTicks() < hookDeadline) {
831 }
832 const bool hookTimedOut = staleStarted && !hookContext.hookCompleted;
833 if (hookTimedOut) {
834 hookContext.cancelled += 1;
835 }
836 Thread::setStateTransitionHook(nullptr);
837 __atomic_store_n(&g_StopEpochHookContext, static_cast<StopEpochHookContext*>(nullptr),
838 __ATOMIC_RELEASE);
839
840 if (hookTimedOut) {
841 if (!staleContext.returned) {
843 }
844 if (process->isSuspended()) {
845 process->resume();
846 }
847 const bool staleJoined = staleTarget->joinForCompletion();
848
849 // Joining the hook owner is the lifetime barrier for its stack context.
850 const bool freshStarted = hookContext.freshStarted == 1;
851 if (freshStarted && !freshContext.returned) {
853 }
854 if (process->isSuspended()) {
855 process->resume();
856 }
857 const bool freshJoined = freshStarted && freshTarget->joinForCompletion();
858 if (!freshStarted) {
859 freshTarget->cullSignalEvent(SIGSTOP);
860 delete freshTarget;
861 }
862 if (process->isSuspended()) {
863 process->resume();
864 }
865
866 const bool cleaned = staleJoined && (!freshStarted || freshJoined);
867 check(false, cleaned ? "the ABA dequeue hook timed out"
868 : "the ABA dequeue hook timed out and thread cleanup failed");
869 delete process;
870 return false;
871 }
872
873 const bool freshStarted = hookContext.freshStarted == 1;
874 const bool freshSuspended = hookContext.freshSuspended == 1 && process->isSuspended();
875 const bool staleJoinedStop = freshSuspended && waitUntilQueued(staleTarget, Thread::ProcessWait);
876 const bool freshJoinedStop = freshSuspended && waitUntilQueued(freshTarget, Thread::ProcessWait);
877 const bool bothStoppedBeforeContinue = !staleContext.returned && !freshContext.returned;
878 Process::ChildTransition freshTransition;
879 const bool freshStopReported = freshSuspended &&
880 takeJobControlTransition(process, freshTransition) &&
881 freshTransition.kind == Process::ChildTransitionKind::Stopped &&
882 freshTransition.stopSignal == SIGSTOP;
883 PosixSubsystem::SignalDeliveryResult continueResult =
884 PosixSubsystem::SignalDeliveryResult::Unavailable;
885 if (freshSuspended) {
886 continueResult = subsystem->queueSignalDelivery(freshTarget, SIGCONT);
887 if (process->isSuspended()) {
888 process->resume();
889 }
890 } else {
891 if (staleStarted && !staleContext.returned) {
893 }
894 if (freshStarted && !freshContext.returned) {
896 }
897 }
898 const bool staleJoined = staleStarted && staleTarget->joinForCompletion();
899 const bool freshJoined = freshStarted && freshTarget->joinForCompletion();
900 if (!staleStarted) {
901 staleTarget->cullSignalEvent(SIGSTOP);
902 delete staleTarget;
903 }
904 if (!freshStarted) {
905 freshTarget->cullSignalEvent(SIGSTOP);
906 delete freshTarget;
907 }
908
909 Process::ChildTransition continuedTransition;
910 const bool continuedReported =
911 takeJobControlTransition(process, continuedTransition) &&
912 continuedTransition.kind == Process::ChildTransitionKind::Continued;
913 const bool finalEpochAdvanced = process->getContinuationEpoch() == (hookContext.epochBefore + 3);
914 const bool passed =
915 check(staleQueued && staleStarted && hookContext.hookCalls == 1 && !hookContext.failures &&
916 hookContext.hookCompleted == 1 && hookContext.continuations == 2 &&
917 hookContext.freshStopQueued == 1 &&
918 hookContext.epochAfter == (hookContext.epochBefore + 2) && finalEpochAdvanced &&
919 staleJoined && freshStarted && freshSuspended && staleJoinedStop &&
920 freshJoinedStop && bothStoppedBeforeContinue && freshStopReported &&
921 continueResult == PosixSubsystem::SignalDeliveryResult::Ignored && freshJoined &&
922 staleContext.entered == 1 && staleContext.returned == 1 &&
923 freshContext.entered == 1 && freshContext.returned == 1 && continuedReported,
924 "a stale stop escaped or invalidated a newer process stop generation");
925 delete process;
926
927 if (passed) {
928 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
929 }
930 return passed;
931}
932
933bool execSignalResetRestampsPendingStop(Process* kernelProcess) {
934 constexpr const char* Test = "exec-signal-reset-restamps-pending-stop";
935 PosixProcess* process = new PosixProcess(kernelProcess);
936 PosixSubsystem* subsystem = new PosixSubsystem;
937 process->setSubsystem(subsystem);
938 process->publish();
939
940 DefaultStopContext context;
941 Thread* target = new Thread(process, waitForDefaultStop, &context, nullptr, false, true, true);
942 target->setName("hosted exec rebound default stop target");
943 pedigree_reset_signals_for_exec(target);
944 installSignalDisposition(*subsystem, SIGCONT, 2);
945
946 const size_t epochBefore = process->getContinuationEpoch();
947 const bool continuedWhileActive = subsystem->queueSignalDelivery(target, SIGCONT) ==
948 PosixSubsystem::SignalDeliveryResult::Ignored;
949 const bool queued = continuedWhileActive && subsystem->queueSignalDelivery(target, SIGSTOP) ==
950 PosixSubsystem::SignalDeliveryResult::Queued;
951 const bool pendingBeforeReset = queued && target->hasSignalEvent(SIGSTOP);
952 if (pendingBeforeReset) {
953 pedigree_reset_signals_for_exec(target);
954 }
955 const bool pendingAfterReset = pendingBeforeReset && target->hasSignalEvent(SIGSTOP);
956 const bool started = pendingAfterReset && target->start();
957
958 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
959 while (started && !process->isSuspended() && !context.returned && Time::getTicks() < deadline) {
961 }
962
963 const bool suspended = process->isSuspended();
964 Process::ChildTransition stoppedTransition;
965 const bool stoppedReported = suspended && takeJobControlTransition(process, stoppedTransition) &&
966 stoppedTransition.kind == Process::ChildTransitionKind::Stopped &&
967 stoppedTransition.stopSignal == SIGSTOP;
968 PosixSubsystem::SignalDeliveryResult continueResult =
969 PosixSubsystem::SignalDeliveryResult::Unavailable;
970 if (suspended) {
971 continueResult = subsystem->queueSignalDelivery(target, SIGCONT);
972 if (process->isSuspended()) {
973 process->resume();
974 }
975 } else if (started && !context.returned) {
977 }
978
979 const bool joined = started && target->joinForCompletion();
980 if (!started) {
981 target->cullSignalEvent(SIGSTOP);
982 delete target;
983 }
984 Process::ChildTransition continuedTransition;
985 const bool continuedReported =
986 takeJobControlTransition(process, continuedTransition) &&
987 continuedTransition.kind == Process::ChildTransitionKind::Continued;
988 const bool passed = check(
989 continuedWhileActive && process->getContinuationEpoch() == (epochBefore + 2) && queued &&
990 pendingBeforeReset && pendingAfterReset && started && suspended && stoppedReported &&
991 continueResult == PosixSubsystem::SignalDeliveryResult::Ignored && joined &&
992 context.entered == 1 && context.returned == 1 && continuedReported,
993 "exec rebound a pending default stop with a stale continuation generation");
994 delete process;
995
996 if (passed) {
997 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
998 }
999 return passed;
1000}
1001
1002int suspendForIgnoredContinue(void* parameter) {
1003 IgnoredContinueContext* context = reinterpret_cast<IgnoredContinueContext*>(parameter);
1004 if (context->blockSignal) {
1005 Processor::information().getCurrentThread()->setSignalMask(static_cast<uint64_t>(1)
1006 << (SIGCONT - 1));
1007 }
1008 context->entered += 1;
1009 context->process->suspend();
1010 context->returned += 1;
1011 return 0;
1012}
1013
1014bool signalContinueResumes(PosixProcess* process, PosixSubsystem* subsystem, int type,
1015 bool blockSignal) {
1016 installSignalDisposition(*subsystem, SIGCONT, type);
1017 IgnoredContinueContext context(process, blockSignal);
1018 Thread* target =
1019 new Thread(process, suspendForIgnoredContinue, &context, nullptr, false, true, true);
1020 target->setName("hosted SIGCONT target");
1021 const bool started = target->start();
1022
1023 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1024 while (started && !process->isSuspended() && Time::getTicks() < deadline) {
1026 }
1027 const bool suspended = process->isSuspended();
1028
1029 g_SignalHandlerCalls = 0;
1030 if (suspended) {
1031 subsystem->sendSignal(target, SIGCONT, false);
1032 }
1033 const bool continuedBySignal = process->getState() == Process::Active;
1034 if (!continuedBySignal) {
1035 process->resume();
1036 }
1037
1038 const bool joined = started && target->joinForCompletion();
1039 if (!started) {
1040 delete target;
1041 }
1042 Process::ChildTransition transition;
1043 bool continuedReported = false;
1044 bool reportedExactlyOnce = false;
1045 Process* parent = process->getParent();
1046 if (parent) {
1047 auto guard = parent->acquireChildStateWait();
1048 continuedReported = process->takePendingChildTransition(true, true, transition) &&
1049 transition.kind == Process::ChildTransitionKind::Continued &&
1050 !transition.stopSignal;
1051 reportedExactlyOnce = !process->takePendingChildTransition(true, true, transition);
1052 }
1053 return started && context.entered == 1 && suspended && continuedBySignal && joined &&
1054 context.returned == 1 && continuedReported && reportedExactlyOnce;
1055}
1056
1057bool signalContinueStillResumes(Process* kernelProcess) {
1058 constexpr const char* Test = "sigcont-resumes-before-disposition";
1059 PosixProcess* process = new PosixProcess(kernelProcess);
1060 PosixSubsystem* subsystem = new PosixSubsystem;
1061 process->setSubsystem(subsystem);
1062 process->publish();
1063
1064 g_SignalHandlerCalls = 0;
1065 const bool ignored = signalContinueResumes(process, subsystem, 2, false);
1066 const bool caughtAndBlocked = signalContinueResumes(process, subsystem, 0, true);
1067 const bool passed =
1068 check(ignored && caughtAndBlocked && g_SignalHandlerCalls == 0,
1069 "an ignored or blocked SIGCONT required handler delivery to resume its target");
1070 delete process;
1071
1072 if (passed) {
1073 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1074 }
1075 return passed;
1076}
1077
1078bool opposingJobControlSignalsCancelAcrossThreads(Process* kernelProcess) {
1079 constexpr const char* Test = "opposing-job-control-signals-cancel";
1080 PosixProcess* process = new PosixProcess(kernelProcess);
1081 PosixSubsystem* subsystem = new PosixSubsystem;
1082 process->setSubsystem(subsystem);
1083 process->publish();
1084
1085 constexpr size_t StopSignals[] = {SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU};
1086 constexpr uint64_t JobControlMask =
1087 (static_cast<uint64_t>(1) << (SIGCONT - 1)) | (static_cast<uint64_t>(1) << (SIGSTOP - 1)) |
1088 (static_cast<uint64_t>(1) << (SIGTSTP - 1)) | (static_cast<uint64_t>(1) << (SIGTTIN - 1)) |
1089 (static_cast<uint64_t>(1) << (SIGTTOU - 1));
1090 installSignalDisposition(*subsystem, SIGCONT, 0);
1091 for (size_t stopSignal : StopSignals) {
1092 installSignalDisposition(*subsystem, stopSignal, 0);
1093 }
1094
1095 Thread* first = new Thread(process, dormantSignalThread, nullptr, nullptr, false, true, true);
1096 Thread* second = new Thread(process, dormantSignalThread, nullptr, nullptr, false, true, true);
1097 first->setSignalMask(JobControlMask);
1098 second->setSignalMask(JobControlMask);
1099
1100 const bool continueQueued = subsystem->queueSignalDelivery(first, SIGCONT) ==
1101 PosixSubsystem::SignalDeliveryResult::Queued &&
1102 first->hasEvent(SIGCONT);
1103 const bool stopQueued = continueQueued && subsystem->queueSignalDelivery(second, SIGTSTP) ==
1104 PosixSubsystem::SignalDeliveryResult::Queued;
1105 const bool stopCancelledContinue =
1106 stopQueued && !first->hasEvent(SIGCONT) && second->hasEvent(SIGTSTP);
1107
1108 bool everyStopQueued = stopCancelledContinue;
1109 for (size_t i = 0; i < sizeof(StopSignals) / sizeof(StopSignals[0]); ++i) {
1110 Thread* target = i % 2 ? second : first;
1111 everyStopQueued &= subsystem->queueSignalDelivery(target, StopSignals[i]) ==
1112 PosixSubsystem::SignalDeliveryResult::Queued;
1113 }
1114 const bool blockedContinueQueued =
1115 everyStopQueued && subsystem->queueSignalDelivery(first, SIGCONT) ==
1116 PosixSubsystem::SignalDeliveryResult::Queued;
1117 bool continueCancelledEveryStop = blockedContinueQueued && first->hasEvent(SIGCONT);
1118 for (size_t stopSignal : StopSignals) {
1119 continueCancelledEveryStop &= !first->hasEvent(stopSignal) && !second->hasEvent(stopSignal);
1120 }
1121
1122 installSignalDisposition(*subsystem, SIGCONT, 2);
1123 bool ignoredSetupQueued = continueCancelledEveryStop;
1124 for (size_t i = 0; i < sizeof(StopSignals) / sizeof(StopSignals[0]); ++i) {
1125 Thread* target = i % 2 ? first : second;
1126 ignoredSetupQueued &= subsystem->queueSignalDelivery(target, StopSignals[i]) ==
1127 PosixSubsystem::SignalDeliveryResult::Queued;
1128 }
1129 const bool ignoredContinue =
1130 ignoredSetupQueued && subsystem->queueSignalDelivery(second, SIGCONT) ==
1131 PosixSubsystem::SignalDeliveryResult::Ignored;
1132 bool ignoredContinueCancelledEveryStop =
1133 ignoredContinue && !first->hasEvent(SIGCONT) && !second->hasEvent(SIGCONT);
1134 for (size_t stopSignal : StopSignals) {
1135 ignoredContinueCancelledEveryStop &=
1136 !first->hasEvent(stopSignal) && !second->hasEvent(stopSignal);
1137 }
1138
1139 Process::ChildTransition transition;
1140 bool redundantResumeWasSilent = false;
1141 {
1142 auto guard = kernelProcess->acquireChildStateWait();
1143 redundantResumeWasSilent = !process->takePendingChildTransition(true, true, transition);
1144 }
1145
1146 const bool firstStarted = first->start();
1147 const bool secondStarted = second->start();
1148 const bool firstJoined = firstStarted && first->joinForCompletion();
1149 const bool secondJoined = secondStarted && second->joinForCompletion();
1150 if (!firstStarted) {
1151 delete first;
1152 }
1153 if (!secondStarted) {
1154 delete second;
1155 }
1156
1157 const bool passed = check(
1158 stopCancelledContinue && continueCancelledEveryStop && ignoredContinueCancelledEveryStop &&
1159 redundantResumeWasSilent && firstStarted && secondStarted && firstJoined && secondJoined,
1160 "a process retained mutually exclusive pending stop and continue signals");
1161 delete process;
1162
1163 if (passed) {
1164 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1165 }
1166 return passed;
1167}
1168
1169struct CaughtContinueContext {
1170 explicit CaughtContinueContext(Process* process)
1171 : process(process), finish(0), entered(0), resumed(0) {}
1172
1173 Process* process;
1174 Semaphore finish;
1175 Atomic<size_t> entered;
1176 Atomic<size_t> resumed;
1177};
1178
1179int suspendForCaughtContinue(void* parameter) {
1180 CaughtContinueContext* context = reinterpret_cast<CaughtContinueContext*>(parameter);
1181 context->entered += 1;
1182 context->process->suspend();
1183 context->resumed += 1;
1184 (void)context->finish.acquire();
1185 return 0;
1186}
1187
1188bool stoppedProcessDefersSignalsUntilContinue(Process* kernelProcess) {
1189 constexpr const char* Test = "stopped-process-defers-signals";
1190 PosixProcess* process = new PosixProcess(kernelProcess);
1191 PosixSubsystem* subsystem = new PosixSubsystem;
1192 process->setSubsystem(subsystem);
1193 process->publish();
1194 installSignalDisposition(*subsystem, SIGUSR1, 0);
1195 installSignalDisposition(*subsystem, SIGCONT, 0, &hostedContinueHandler);
1196
1197 IgnoredSignalWaitContext ordinaryContext;
1198 Thread* ordinary =
1199 new Thread(process, waitThroughIgnoredSignal, &ordinaryContext, nullptr, false, true, true);
1200 ordinary->setName("hosted stopped ordinary-signal target");
1201 const bool ordinaryStarted = ordinary->start();
1202 const bool ordinaryWaiting = ordinaryStarted && waitUntilQueued(ordinary, Thread::SemWait);
1203
1204 CaughtContinueContext continueContext(process);
1205 Thread* continuer =
1206 new Thread(process, suspendForCaughtContinue, &continueContext, nullptr, false, true, true);
1207 continuer->setName("hosted caught SIGCONT target");
1208 const bool continuerStarted = ordinaryWaiting && continuer->start();
1209 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1210 while (continuerStarted && !process->isSuspended() && Time::getTicks() < deadline) {
1212 }
1213 const bool suspended = process->isSuspended();
1214
1215 g_SignalHandlerCalls = 0;
1216 g_ContinueHandlerCalls = 0;
1217 g_ContinueHandlerObservedActive = 0;
1218 const PosixSubsystem::SignalDeliveryResult ordinaryResult =
1219 suspended ? subsystem->queueSignalDelivery(ordinary, SIGUSR1)
1220 : PosixSubsystem::SignalDeliveryResult::Unavailable;
1221 for (size_t attempt = 0; attempt < 32 && !ordinaryContext.returned; ++attempt) {
1223 }
1224 Thread::WaitDebugInfo wait = {};
1225 const bool ordinaryStayedPending =
1226 ordinaryResult == PosixSubsystem::SignalDeliveryResult::Queued &&
1227 ordinary->hasEvent(SIGUSR1) && !ordinaryContext.returned && !g_SignalHandlerCalls &&
1228 ordinary->getWaitDebugInfo(wait) && wait.queued && process->isSuspended();
1229
1230 const PosixSubsystem::SignalDeliveryResult continueResult =
1231 ordinaryStayedPending ? subsystem->queueSignalDelivery(continuer, SIGCONT)
1232 : PosixSubsystem::SignalDeliveryResult::Unavailable;
1233 const bool continuedBySignal = process->getState() == Process::Active;
1234 const Time::Timestamp deliveryDeadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1235 while ((!ordinaryContext.returned || !g_ContinueHandlerCalls) &&
1236 Time::getTicks() < deliveryDeadline) {
1238 }
1239 const bool deliveredByContinue = ordinaryContext.returned && g_ContinueHandlerCalls;
1240
1241 if (continuerStarted && !suspended) {
1243 }
1244 if (!ordinaryContext.returned) {
1245 ordinaryContext.gate.release();
1246 }
1247 continueContext.finish.release();
1248 process->resume();
1249 const bool ordinaryJoined = ordinaryStarted && ordinary->joinForCompletion();
1250 const bool continuerJoined = continuerStarted && continuer->joinForCompletion();
1251 // A timeout can race just ahead of the worker's Active -> Suspended CAS.
1252 // Termination makes that late wait return; this second resume cleans the
1253 // process state after the worker is guaranteed off-stack.
1254 process->resume();
1255 if (!ordinaryStarted) {
1256 delete ordinary;
1257 }
1258 if (!continuerStarted) {
1259 delete continuer;
1260 }
1261
1262 const bool passed = check(
1263 ordinaryStarted && ordinaryWaiting && continuerStarted && suspended &&
1264 ordinaryStayedPending && continueResult == PosixSubsystem::SignalDeliveryResult::Queued &&
1265 continuedBySignal && deliveredByContinue && ordinaryJoined && continuerJoined &&
1266 ordinaryContext.entered == 1 && ordinaryContext.returned == 1 &&
1267 ordinaryContext.acquired == 0 && ordinaryContext.error == Semaphore::Interrupted &&
1268 ordinaryContext.interruption == Thread::InterruptedBySignal &&
1269 continueContext.entered == 1 && continueContext.resumed == 1 &&
1270 g_SignalHandlerCalls == 1 && g_ContinueHandlerCalls == 1 &&
1271 g_ContinueHandlerObservedActive == 1,
1272 "a stopped signal escaped early, remained stranded, or ran SIGCONT before Active");
1273 delete process;
1274
1275 if (passed) {
1276 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1277 }
1278 return passed;
1279}
1280#endif
1281
1282bool execPreservesNestedSignalMask(Thread* thread) {
1283 constexpr const char* Test = "exec-preserves-nested-signal-mask";
1284 constexpr uint64_t TemporaryMask = static_cast<uint64_t>(1) << (HostedSignalNumber + 1);
1285 constexpr uint64_t NestedTemporaryMask = static_cast<uint64_t>(1) << (HostedSignalNumber + 2);
1286 constexpr uint64_t EffectiveMask = (static_cast<uint64_t>(1) << (HostedSignalNumber - 1)) |
1287 (static_cast<uint64_t>(1) << (HostedSignalNumber + 3));
1288 const uint64_t originalMask = thread->getSignalMask();
1289 const Thread::InterruptionReason originalInterruption = thread->getInterruptionReason();
1290 const Thread::AlternateSignalStack originalAlternate = thread->getAlternateSignalStack();
1291 const size_t originalLevel = thread->getStateLevel();
1292
1293 bool baseTemporaryMaskActive = false;
1294 bool nestedTemporaryMaskActive = false;
1295 bool firstPushed = false;
1296 bool secondPushed = false;
1297 bool execScopePreserved = false;
1298 bool execScopeReleased = false;
1299 if (!originalLevel) {
1300 Thread::TemporarySignalMask baseTemporaryMask(*thread, TemporaryMask);
1301 baseTemporaryMaskActive = thread->hasActiveTemporarySignalMask();
1302 firstPushed = thread->pushState() != nullptr;
1303 if (firstPushed) {
1304 {
1305 Thread::TemporarySignalMask nestedTemporaryMask(*thread, NestedTemporaryMask);
1306 nestedTemporaryMaskActive = thread->hasActiveTemporarySignalMask();
1307 secondPushed = thread->pushState() != nullptr;
1308 if (secondPushed) {
1309 thread->setSignalMask(EffectiveMask);
1310 Thread::AlternateSignalStack& alternate = thread->getAlternateSignalStack();
1311 alternate.base = 0x200000;
1312 alternate.size = 0x8000;
1313 alternate.enabled = true;
1314 alternate.inUse = true;
1315 {
1316 Uninterruptible execScope;
1317 thread->prepareSignalStateForExec();
1318 execScopePreserved = thread->eventsDeferred();
1319 }
1320 execScopeReleased = !thread->eventsDeferred();
1321 thread->abandonCurrentState(false);
1322 }
1323 }
1324 if (thread->getStateLevel() > originalLevel) {
1325 thread->abandonCurrentState(false);
1326 }
1327 }
1328 }
1329
1330 const Thread::AlternateSignalStack resetAlternate = thread->getAlternateSignalStack();
1331 const bool passed = check(
1332 baseTemporaryMaskActive && nestedTemporaryMaskActive && firstPushed && secondPushed &&
1333 execScopePreserved && execScopeReleased && thread->getStateLevel() == originalLevel &&
1334 thread->getSignalMask() == EffectiveMask && !resetAlternate.base &&
1335 !resetAlternate.size && !resetAlternate.enabled && !resetAlternate.inUse &&
1336 !thread->hasActiveTemporarySignalMask() &&
1337 thread->getInterruptionReason() == Thread::NotInterrupted,
1338 "exec lost the active handler mask or retained an outer temporary-mask scope");
1339
1340 thread->setSignalMask(originalMask);
1341 thread->setInterruptionReason(originalInterruption);
1342 thread->getAlternateSignalStack() = originalAlternate;
1343 if (passed) {
1344 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1345 }
1346 return passed;
1347}
1348
1349bool invalidUserHandlerDeliveryFailsClosed(Thread* thread) {
1350 constexpr const char* Test = "invalid-user-handler-delivery";
1351 g_SignalHandlerCalls = 0;
1352
1353 SignalEvent* event =
1354 new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber, ~0UL,
1355 0, true, true, Event::HandlerPrivilege::User);
1356 const bool queued = thread->sendEvent(event);
1357 if (!queued) {
1358 delete event;
1359 } else {
1360 thread->getScheduler()->checkEventState(0);
1361 }
1362
1363 const bool passed = check(queued && !g_SignalHandlerCalls && !thread->getStateLevel(),
1364 "an unmapped user handler executed or retained scheduler state");
1365 if (passed) {
1366 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1367 }
1368 return passed;
1369}
1370
1371struct SignalContext {
1372 SignalContext(Thread* target, size_t debugState, Semaphore* releaseAfterDelivery = nullptr)
1373 : target(target),
1374 debugState(debugState),
1375 releaseAfterDelivery(releaseAfterDelivery),
1376 published(0),
1377 sent(0),
1378 released(0) {}
1379
1380 Thread* target;
1381 size_t debugState;
1382 Semaphore* releaseAfterDelivery;
1383 Atomic<size_t> published;
1384 Atomic<size_t> sent;
1385 Atomic<size_t> released;
1386};
1387
1388struct DefaultActionSemaphoreContext {
1389 DefaultActionSemaphoreContext(Thread* target, Semaphore* gate)
1390 : target(target), gate(gate), published(0), sent(0), released(0) {}
1391
1392 Thread* target;
1393 Semaphore* gate;
1394 Atomic<size_t> published;
1395 Atomic<size_t> sent;
1396 Atomic<size_t> released;
1397};
1398
1399struct TemporaryMaskMutexContext {
1400 TemporaryMaskMutexContext()
1401 : mutex(), holderReady(0), releaseHolder(0), holderAcquired(0), holderReturned(0) {}
1402
1403 Mutex mutex;
1404 Semaphore holderReady;
1405 Semaphore releaseHolder;
1406 Atomic<size_t> holderAcquired;
1407 Atomic<size_t> holderReturned;
1408};
1409
1410struct SemaphoreWakeCollisionContext {
1411 SemaphoreWakeCollisionContext()
1412 : semaphore(0),
1413 waiter(nullptr),
1414 entered(0),
1415 returned(0),
1416 acquired(0),
1417 error(Semaphore::NoError),
1418 interruption(Thread::NotInterrupted),
1419 rescueWaits(0) {}
1420
1421 Semaphore semaphore;
1422 Thread* waiter;
1423 Atomic<size_t> entered;
1424 Atomic<size_t> returned;
1425 Atomic<size_t> acquired;
1426 Atomic<size_t> error;
1427 Atomic<size_t> interruption;
1428 Atomic<size_t> rescueWaits;
1429};
1430
1431SemaphoreWakeCollisionContext* g_SemaphoreWakeCollision = nullptr;
1432
1433struct ConditionWakeCollisionContext {
1434 ConditionWakeCollisionContext()
1435 : waiter(nullptr),
1436 predicate(0),
1437 entered(0),
1438 returned(0),
1439 waits(0),
1440 lastResult(0),
1441 error(ConditionVariable::NoError),
1442 rescueWaits(0) {}
1443
1444 Mutex mutex;
1445 ConditionVariable condition;
1446 Thread* waiter;
1447 Atomic<size_t> predicate;
1448 Atomic<size_t> entered;
1449 Atomic<size_t> returned;
1450 Atomic<size_t> waits;
1451 Atomic<size_t> lastResult;
1452 Atomic<size_t> error;
1453 Atomic<size_t> rescueWaits;
1454};
1455
1456ConditionWakeCollisionContext* g_ConditionWakeCollision = nullptr;
1457
1458struct EventDrainContext {
1459 explicit EventDrainContext(Event* event) : event(event), entered(0), completed(0) {}
1460
1461 Event* event;
1462 Atomic<size_t> entered;
1463 Atomic<size_t> completed;
1464};
1465
1466int drainEventRegistrations(void* parameter) {
1467 EventDrainContext* context = reinterpret_cast<EventDrainContext*>(parameter);
1468 context->entered += 1;
1469 context->event->waitForDeliveries();
1470 context->completed += 1;
1471 return 0;
1472}
1473
1474int interruptPublishedWait(void* parameter) {
1475 SignalContext* context = reinterpret_cast<SignalContext*>(parameter);
1476 if (waitUntilQueued(context->target, context->debugState)) {
1477 context->published += 1;
1478 }
1479
1480 SignalEvent* event = new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler),
1481 HostedSignalNumber, ~0UL, 0, true, true);
1482 if (context->target->sendEvent(event)) {
1483 context->sent += 1;
1484 }
1485
1486 if (context->releaseAfterDelivery) {
1487 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1488 while (!g_SignalHandlerCalls && Time::getTicks() < deadline) {
1490 }
1491 context->releaseAfterDelivery->release();
1492 context->released += 1;
1493 }
1494 return 0;
1495}
1496
1497int holdTemporaryMaskMutex(void* parameter) {
1498 TemporaryMaskMutexContext* context = reinterpret_cast<TemporaryMaskMutexContext*>(parameter);
1499 const bool acquired = context->mutex.acquire();
1500 if (acquired) {
1501 context->holderAcquired += 1;
1502 }
1503 context->holderReady.release();
1504
1505 bool released = false;
1506 if (acquired) {
1507 released = context->releaseHolder.acquireForCompletion();
1508 context->mutex.release();
1509 }
1510 context->holderReturned += 1;
1511 return acquired && released ? 0 : 1;
1512}
1513
1514int waitForSemaphoreWakeCollision(void* parameter) {
1515 SemaphoreWakeCollisionContext* context =
1516 reinterpret_cast<SemaphoreWakeCollisionContext*>(parameter);
1517 context->entered += 1;
1518
1519 Semaphore::SemaphoreError error = Semaphore::NoError;
1520 const bool acquired = context->semaphore.acquireWithError(1, 0, 0, error);
1521 context->acquired = acquired ? 1 : 0;
1522 context->error = static_cast<size_t>(error);
1523
1524 Thread* thread = Processor::information().getCurrentThread();
1525 context->interruption = static_cast<size_t>(thread->getInterruptionReason());
1526 thread->clearInterruption();
1527 context->returned += 1;
1528 return 0;
1529}
1530
1531void semaphoreWakeCollisionRescue(WaitQueue* queue, Thread* thread,
1532 const WaitQueue::Channel& channel, size_t debugState) {
1533 (void)queue;
1534 SemaphoreWakeCollisionContext* context = g_SemaphoreWakeCollision;
1535 if (!context || thread != context->waiter || channel.owner != &context->semaphore ||
1536 channel.value || debugState != Thread::SemWait) {
1537 return;
1538 }
1539
1540 context->rescueWaits += 1;
1541 context->semaphore.release();
1542}
1543
1544int waitForConditionWakeCollision(void* parameter) {
1545 ConditionWakeCollisionContext* context =
1546 reinterpret_cast<ConditionWakeCollisionContext*>(parameter);
1547 if (!context->mutex.acquireForCompletion()) {
1548 return 1;
1549 }
1550 context->entered += 1;
1551
1552 ConditionVariable::Error error = ConditionVariable::NoError;
1553 bool result = true;
1554 while (!context->predicate && result) {
1555 context->waits += 1;
1556 result = context->condition.wait(context->mutex, error);
1557 }
1558 context->lastResult = result ? 1 : 0;
1559 context->error = static_cast<size_t>(error);
1560 context->mutex.release();
1561 context->returned += 1;
1562 return 0;
1563}
1564
1565void conditionWakeCollisionRescue(WaitQueue* queue, Thread* thread,
1566 const WaitQueue::Channel& channel, size_t debugState) {
1567 (void)queue;
1568 ConditionWakeCollisionContext* context = g_ConditionWakeCollision;
1569 if (!context || thread != context->waiter || channel.owner || channel.value ||
1570 debugState != Thread::CondWait) {
1571 return;
1572 }
1573
1574 context->rescueWaits += 1;
1575 if (context->mutex.acquireForCompletion()) {
1576 context->predicate = 1;
1577 context->condition.signal();
1578 context->mutex.release();
1579 }
1580}
1581
1582Thread* startInterrupter(SignalContext& context) {
1583 Thread* thread = new Thread(Scheduler::instance().getKernelProcess(), interruptPublishedWait,
1584 &context, nullptr, false, true);
1585 thread->setName("hosted signal interrupter");
1586 return thread;
1587}
1588
1589int publishDefaultActionDuringSemaphoreWait(void* parameter) {
1590 DefaultActionSemaphoreContext* context =
1591 reinterpret_cast<DefaultActionSemaphoreContext*>(parameter);
1592 if (waitUntilQueued(context->target, Thread::SemWait)) {
1593 context->published += 1;
1594 }
1595
1596 SignalEvent* event = new SignalEvent(
1597 reinterpret_cast<uintptr_t>(&hostedDefaultActionHandler), HostedSignalNumber, ~0UL, 0, true,
1598 true, Event::HandlerPrivilege::Kernel, SignalEvent::DeliveryDisposition::DefaultAction);
1599 if (context->target->sendEvent(event)) {
1600 context->sent += 1;
1601 } else {
1602 delete event;
1603 }
1604
1605 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
1606 while (!g_DefaultActionHandlerCalls && Time::getTicks() < deadline) {
1608 }
1609 context->gate->release();
1610 context->released += 1;
1611 return 0;
1612}
1613
1614bool temporarySignalMaskNestedPrequeued(Thread* thread) {
1615 constexpr const char* Test = "temporary-signal-mask-nested-prequeued";
1616 constexpr uint64_t SignalBit = static_cast<uint64_t>(1) << (HostedSignalNumber - 1);
1617 const uint64_t originalMask = thread->getSignalMask();
1618 const uint64_t blockedMask = originalMask | SignalBit;
1619 const uint64_t temporaryMask = blockedMask & ~SignalBit;
1620 const size_t initialStateLevel = thread->getStateLevel();
1621
1622 thread->setSignalMask(blockedMask);
1623 thread->clearInterruption();
1624 g_NestedWaitHandlerCalls = 0;
1625 g_NestedWaitHandlerLevel = 0;
1626 g_NestedWaitReturned = 0;
1627 g_NestedSignalHandlerCalls = 0;
1628 g_NestedSignalHandlerLevel = 0;
1629
1630 HostedNestedWaitEvent outerEvent;
1631 SignalEvent signalEvent(reinterpret_cast<uintptr_t>(&hostedNestedSignalHandler),
1632 HostedSignalNumber);
1633 const bool outerQueued = thread->sendEvent(&outerEvent);
1634 const bool signalQueued = outerQueued && thread->sendEvent(&signalEvent);
1635
1636 bool activeMaskObserved = false;
1637 bool nestedStateRestored = false;
1638 bool interrupted = false;
1639 bool exactMaskRestored = false;
1640 bool interruptionConsumed = false;
1641 if (outerQueued && signalQueued) {
1642 Thread::TemporarySignalMask signalWait(*thread, temporaryMask);
1643 activeMaskObserved = thread->getSignalMask() == temporaryMask;
1644 thread->waitForEvent();
1645 nestedStateRestored = thread->getStateLevel() == initialStateLevel;
1646 interrupted = signalWait.finish();
1647 exactMaskRestored = thread->getSignalMask() == blockedMask;
1648 interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
1649 }
1650
1651 const bool queuesDrained = !thread->hasEvent(&outerEvent) && !thread->hasEvent(&signalEvent);
1652 if (thread->hasEvent(&outerEvent)) {
1653 thread->cullEvent(&outerEvent);
1654 }
1655 if (thread->hasEvent(&signalEvent)) {
1656 thread->cullEvent(&signalEvent);
1657 }
1658 thread->setSignalMask(originalMask);
1659 thread->clearInterruption();
1660
1661 const bool passed = check(
1662 outerQueued && signalQueued && activeMaskObserved && nestedStateRestored && interrupted &&
1663 exactMaskRestored && interruptionConsumed && queuesDrained &&
1664 g_NestedWaitHandlerCalls == 1 && g_NestedWaitHandlerLevel == (initialStateLevel + 1) &&
1665 g_NestedWaitReturned == 1 && g_NestedSignalHandlerCalls == 1 &&
1666 g_NestedSignalHandlerLevel == (initialStateLevel + 2),
1667 "a nested prequeued signal missed its owning temporary mask or exact restoration");
1668 if (passed) {
1669 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1670 }
1671 return passed;
1672}
1673
1674bool temporarySignalMaskAcrossMutex(Thread* thread) {
1675 constexpr const char* Test = "temporary-signal-mask-across-mutex";
1676 constexpr uint64_t SignalBit = static_cast<uint64_t>(1) << (HostedSignalNumber - 1);
1677 const uint64_t originalMask = thread->getSignalMask();
1678 const uint64_t blockedMask = originalMask | SignalBit;
1679 const uint64_t temporaryMask = blockedMask & ~SignalBit;
1680
1681 thread->setSignalMask(blockedMask);
1682 thread->clearInterruption();
1683 g_SignalHandlerCalls = 0;
1684
1685 TemporaryMaskMutexContext context;
1686 Thread* holder = new Thread(Scheduler::instance().getKernelProcess(), holdTemporaryMaskMutex,
1687 &context, nullptr, false, true, true);
1688 holder->setName("hosted temporary signal-mask mutex holder");
1689 const bool holderStarted = holder->start();
1690 const bool holderReady = holderStarted && context.holderReady.acquireForCompletion();
1691
1692 bool activeMaskObserved = false;
1693 bool mutexAcquired = false;
1694 bool stickyAfterMutex = false;
1695 bool interruptibleWaitReturned = false;
1696 Semaphore::SemaphoreError waitError = Semaphore::NoError;
1697 bool interrupted = false;
1698 bool exactMaskRestored = false;
1699 bool interruptionConsumed = false;
1700 bool interrupterJoined = false;
1701 SignalContext signalContext(thread, Thread::SemWait, &context.releaseHolder);
1702 if (holderReady && context.holderAcquired == 1) {
1703 Thread::TemporarySignalMask signalWait(*thread, temporaryMask);
1704 activeMaskObserved = thread->getSignalMask() == temporaryMask;
1705 Thread* interrupter = startInterrupter(signalContext);
1706
1707 mutexAcquired = context.mutex.acquire();
1708 stickyAfterMutex = thread->hasTemporarySignalWaitInterruption();
1709 if (mutexAcquired) {
1710 context.mutex.release();
1711 }
1712
1713 if (stickyAfterMutex) {
1714 Semaphore interruptible(0);
1715 interruptibleWaitReturned = !interruptible.acquireWithError(1, 0, 0, waitError);
1716 }
1717
1718 interrupted = signalWait.finish();
1719 exactMaskRestored = thread->getSignalMask() == blockedMask;
1720 interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
1721 interrupterJoined = interrupter->join();
1722 } else {
1723 context.releaseHolder.release();
1724 }
1725
1726 const bool holderJoined = holderStarted && holder->join();
1727 if (!holderStarted) {
1728 delete holder;
1729 }
1730 thread->setSignalMask(originalMask);
1731 thread->clearInterruption();
1732
1733 const bool passed = check(
1734 holderStarted && holderReady && context.holderAcquired == 1 && context.holderReturned == 1 &&
1735 activeMaskObserved && mutexAcquired && stickyAfterMutex && interruptibleWaitReturned &&
1736 waitError == Semaphore::Interrupted && interrupted && exactMaskRestored &&
1737 interruptionConsumed && interrupterJoined && holderJoined &&
1738 signalContext.published == 1 && signalContext.sent == 1 && signalContext.released == 1 &&
1739 g_SignalHandlerCalls == 1,
1740 "a non-interruptible Mutex lost or consumed its armed temporary-wait signal");
1741 if (passed) {
1742 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1743 }
1744 return passed;
1745}
1746
1747bool temporarySignalMaskIgnoresDefaultAction(Thread* thread) {
1748 constexpr const char* Test = "temporary-signal-mask-default-action";
1749 const uint64_t originalMask = thread->getSignalMask();
1750 thread->clearInterruption();
1751 g_DefaultActionHandlerCalls = 0;
1752
1753 Semaphore gate(0);
1754 DefaultActionSemaphoreContext context(thread, &gate);
1755 Thread* publisher =
1756 new Thread(Scheduler::instance().getKernelProcess(), publishDefaultActionDuringSemaphoreWait,
1757 &context, nullptr, false, true);
1758 publisher->setName("hosted default-action signal publisher");
1759
1760 Semaphore::SemaphoreError error = Semaphore::NoError;
1761 bool acquired = false;
1762 bool interrupted = true;
1763 {
1764 Thread::TemporarySignalMask signalWait(*thread, originalMask);
1765 acquired = gate.acquireWithError(1, 0, 0, error);
1766 interrupted = signalWait.finish();
1767 }
1768 const bool joined = publisher->join();
1769 const bool interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
1770 thread->setSignalMask(originalMask);
1771 thread->clearInterruption();
1772
1773 const bool passed =
1774 check(acquired && error == Semaphore::NoError && !interrupted && joined &&
1775 interruptionConsumed && context.published == 1 && context.sent == 1 &&
1776 context.released == 1 && g_DefaultActionHandlerCalls == 1,
1777 "a default signal action terminated a temporary-mask Semaphore wait");
1778 if (passed) {
1779 NOTICE("HOSTED-WAIT-TEST: PASS " << Test);
1780 }
1781 return passed;
1782}
1783
1784bool conditionVariableSignalInterruption(Thread* thread) {
1785 ConditionVariable condition;
1786 Mutex mutex;
1787 SignalContext context(thread, Thread::CondWait);
1788
1789 if (!mutex.acquire()) {
1790 return check(false, "the ConditionVariable mutex was unavailable");
1791 }
1792 bool passed = true;
1793 g_SignalHandlerCalls = 0;
1794 Thread* interrupter = startInterrupter(context);
1795
1796 ConditionVariable::Error error = ConditionVariable::NoError;
1797 const bool waited = condition.wait(mutex, error);
1798 const bool mutexHeld = mutex.isOwnedByCurrentThread();
1799 if (mutexHeld) {
1800 mutex.release();
1801 }
1802 const bool joined = interrupter->join();
1803
1804 passed &= check(!waited && error == ConditionVariable::Interrupted,
1805 "ConditionVariable did not report Interrupted");
1806 passed &= check(mutexHeld, "ConditionVariable did not reacquire its mutex");
1807 passed &=
1808 check(joined && context.published == 1 && context.sent == 1 && g_SignalHandlerCalls == 1,
1809 "ConditionVariable wait did not receive one published signal");
1810 return passed;
1811}
1812
1813bool bufferSignalInterruption(Thread* thread) {
1814 Buffer<char> buffer(8);
1815 SignalContext context(thread, Thread::CondWait);
1816
1817 thread->clearInterruption();
1818 g_SignalHandlerCalls = 0;
1819 Thread* interrupter = startInterrupter(context);
1820
1821 char value = 0;
1822 const size_t read = buffer.read(&value, 1, true);
1823 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1824 thread->clearInterruption();
1825 const bool joined = interrupter->join();
1826
1827 return check(read == 0 && reason == Thread::InterruptedBySignal && joined &&
1828 context.published == 1 && context.sent == 1 && g_SignalHandlerCalls == 1,
1829 "Buffer did not preserve its signal interruption");
1830}
1831
1832bool semaphoreSignalInterruption(Thread* thread) {
1833 Semaphore semaphore(0);
1834 SignalContext context(thread, Thread::SemWait);
1835
1836 thread->clearInterruption();
1837 g_SignalHandlerCalls = 0;
1838 Thread* interrupter = startInterrupter(context);
1839
1840 Semaphore::SemaphoreError error = Semaphore::NoError;
1841 const bool acquired = semaphore.acquireWithError(1, 0, 0, error);
1842 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1843 thread->clearInterruption();
1844 const bool joined = interrupter->join();
1845
1846 return check(!acquired && error == Semaphore::Interrupted &&
1847 reason == Thread::InterruptedBySignal && joined && context.published == 1 &&
1848 context.sent == 1 && g_SignalHandlerCalls == 1,
1849 "Semaphore did not preserve its signal interruption");
1850}
1851
1852bool semaphoreSignalAfterOrdinaryWake() {
1853 SemaphoreWakeCollisionContext context;
1854 Thread* waiter = new Thread(Scheduler::instance().getKernelProcess(),
1855 waitForSemaphoreWakeCollision, &context, nullptr, false, true);
1856 waiter->setName("hosted semaphore wake/signal collision");
1857 context.waiter = waiter;
1858
1859 const bool queued = waitUntilQueued(waiter, Thread::SemWait);
1860 g_SignalHandlerCalls = 0;
1861 g_SemaphoreWakeCollision = &context;
1862 WaitQueue::setBeforeBlockHook(semaphoreWakeCollisionRescue);
1863
1864 // Win waiter.reason with an ordinary release, then make that release
1865 // unavailable and publish a signal before the waiter can run.
1866 context.semaphore.release();
1867 const bool releaseConsumed = context.semaphore.tryAcquire();
1868 SignalEvent* event = new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler),
1869 HostedSignalNumber, ~0UL, 0, true, true);
1870 const bool sent = waiter->sendEvent(event);
1871 if (!sent) {
1872 delete event;
1873 }
1874
1875 const bool joined = waiter->join();
1876 WaitQueue::setBeforeBlockHook(nullptr);
1877 g_SemaphoreWakeCollision = nullptr;
1878
1879 const bool passed = check(queued && releaseConsumed && sent && joined && context.entered == 1 &&
1880 context.returned == 1 && context.acquired == 0 &&
1881 context.error == Semaphore::Interrupted &&
1882 context.interruption == Thread::InterruptedBySignal &&
1883 context.rescueWaits == 0 && g_SignalHandlerCalls == 1,
1884 "Semaphore re-blocked after a signal lost the waiter.reason race");
1885 if (passed) {
1886 NOTICE(
1887 "HOSTED-WAIT-TEST: PASS "
1888 "semaphore-signal-after-ordinary-wake");
1889 }
1890 return passed;
1891}
1892
1893bool conditionSignalAfterOrdinaryWake() {
1894 ConditionWakeCollisionContext context;
1895 Thread* waiter = new Thread(Scheduler::instance().getKernelProcess(),
1896 waitForConditionWakeCollision, &context, nullptr, false, true);
1897 waiter->setName("hosted condition wake/signal collision");
1898 context.waiter = waiter;
1899
1900 const bool queued = waitUntilQueued(waiter, Thread::CondWait);
1901 g_SignalHandlerCalls = 0;
1902 g_ConditionWakeCollision = &context;
1903 WaitQueue::setBeforeBlockHook(conditionWakeCollisionRescue);
1904
1905 // The condition signal deliberately does not satisfy the predicate. A
1906 // signal event published after this wake must still become the result.
1907 context.condition.signal();
1908 SignalEvent* event = new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler),
1909 HostedSignalNumber, ~0UL, 0, true, true);
1910 const bool sent = waiter->sendEvent(event);
1911 if (!sent) {
1912 delete event;
1913 }
1914
1915 const bool joined = waiter->join();
1916 WaitQueue::setBeforeBlockHook(nullptr);
1917 g_ConditionWakeCollision = nullptr;
1918
1919 const bool passed =
1920 check(queued && sent && joined && context.entered == 1 && context.returned == 1 &&
1921 context.waits == 1 && context.lastResult == 0 &&
1922 context.error == ConditionVariable::Interrupted && context.rescueWaits == 0 &&
1923 g_SignalHandlerCalls == 1,
1924 "ConditionVariable re-blocked after a signal lost the waiter.reason race");
1925 if (passed) {
1926 NOTICE(
1927 "HOSTED-WAIT-TEST: PASS "
1928 "condition-signal-after-ordinary-wake");
1929 }
1930 return passed;
1931}
1932
1933bool completionSemaphoreSignalDeferral(Thread* thread) {
1934 Semaphore semaphore(0);
1935 SignalContext context(thread, Thread::SemWait, &semaphore);
1936
1937 thread->clearInterruption();
1938 g_SignalHandlerCalls = 0;
1939 Thread* interrupter = startInterrupter(context);
1940
1941 const bool acquired = semaphore.acquireForCompletion();
1942 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1943 thread->clearInterruption();
1944 const bool joined = interrupter->join();
1945
1946 return check(acquired && reason == Thread::InterruptedBySignal && joined &&
1947 context.published == 1 && context.sent == 1 && context.released == 1 &&
1948 g_SignalHandlerCalls == 1 && semaphore.getValue() == 0,
1949 "a completion semaphore returned before its signal-delayed release");
1950}
1951
1952bool ringBufferSignalInterruption(Thread* thread) {
1953 RingBuffer<char> buffer(1);
1954 SignalContext context(thread, Thread::CondWait);
1955
1956 thread->clearInterruption();
1957 g_SignalHandlerCalls = 0;
1958 Thread* interrupter = startInterrupter(context);
1959
1960 Time::Timestamp timeout = Time::Infinity;
1961 char value = 0;
1962 RingBuffer<char>::Error error = RingBuffer<char>::NoError;
1963 const bool read = buffer.read(value, timeout, error);
1964 const Thread::InterruptionReason reason = thread->getInterruptionReason();
1965 thread->clearInterruption();
1966 const bool joined = interrupter->join();
1967
1968 return check(!read && error == RingBuffer<char>::Interrupted &&
1969 reason == Thread::InterruptedBySignal && joined && context.published == 1 &&
1970 context.sent == 1 && g_SignalHandlerCalls == 1,
1971 "RingBuffer did not preserve its signal interruption");
1972}
1973
1974bool ringBufferMonitorCull(Thread* thread) {
1975 RingBuffer<char> buffer(1);
1976 SignalEvent event(reinterpret_cast<uintptr_t>(&hostedSignalHandler), HostedSignalNumber);
1977
1978 buffer.monitor(thread, &event);
1979 buffer.cullMonitorTargets(thread);
1980
1981 return check(!buffer.dataReady() && buffer.canWrite(),
1982 "RingBuffer monitor culling retained its internal mutex");
1983}
1984
1985bool ringBufferMonitorRetirement(Thread* thread) {
1986 RingBuffer<char> buffer(1);
1987 HostedMonitorEvent event;
1988 buffer.monitor(thread, &event);
1989
1990 EventDrainContext context(&event);
1991 Thread* drainer = new Thread(Scheduler::instance().getKernelProcess(), drainEventRegistrations,
1992 &context, nullptr, false, true);
1993 drainer->setName("hosted RingBuffer monitor retirement");
1994
1995 const bool closePublished = waitUntilQueued(drainer, Thread::EventWait);
1996
1997 // Model an already-dispatched callback trying to re-arm after its owner
1998 // closed event admission. A distinct target lets the original cull prove
1999 // that no second registration was accepted.
2000 buffer.monitor(drainer, &event);
2001 buffer.cullMonitorTargets(thread);
2002
2003 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
2004 while (!context.completed && Time::getTicks() < deadline) {
2006 }
2007 const bool rearmRejected = context.completed == 1;
2008
2009 // Keep a failing regression from leaving its drain thread behind.
2010 if (!rearmRejected) {
2011 buffer.cullMonitorTargets(drainer);
2012 }
2013 const bool joined = drainer->join();
2014
2015 return check(context.entered == 1 && closePublished && rearmRejected && joined,
2016 "RingBuffer accepted a monitor registration after Event retirement");
2017}
2018
2019bool ringBufferMonitorDestructor(Thread* thread) {
2020 const size_t destructionsBefore = g_MonitorEventDestructions;
2021 HostedMonitorEvent* event = new HostedMonitorEvent;
2022 {
2023 RingBuffer<char> buffer(1);
2024 buffer.monitor(thread, event);
2025 }
2026
2027 // RingBuffer closure legitimately queued one final readiness event. Keep
2028 // retirement pinned while removing that delivery so deletion now depends
2029 // only on whether the destroyed RingBuffer released its source lease.
2030 const bool closeNotified = event->pendingCount() == 1 && thread->hasEvent(event);
2031 {
2032 Event::Retirement retirement;
2033 event->beginRetirement(retirement);
2034 thread->cullEvent(event);
2035 }
2036 return check(closeNotified && g_MonitorEventDestructions == (destructionsBefore + 1),
2037 "RingBuffer destruction retained its Event source lease after the "
2038 "queued close notification drained");
2039}
2040
2041bool delaySignalInterruption(Thread* thread) {
2042 SignalContext context(thread, Thread::EventWait);
2043
2044 thread->clearInterruption();
2045 g_SignalHandlerCalls = 0;
2046 Thread* interrupter = startInterrupter(context);
2047
2048 const bool delayed = Time::delay(5 * Time::Multiplier::Second);
2049 const Thread::InterruptionReason reason = thread->getInterruptionReason();
2050 thread->clearInterruption();
2051 const bool joined = interrupter->join();
2052
2053 return check(!delayed && reason == Thread::InterruptedBySignal && joined &&
2054 context.published == 1 && context.sent == 1 && g_SignalHandlerCalls == 1,
2055 "Time::delay did not preserve its signal interruption");
2056}
2057
2058bool prequeuedDelaySignalInterruption(Thread* thread) {
2059 thread->clearInterruption();
2060 g_SignalHandlerCalls = 0;
2061
2062 SignalEvent* event = new SignalEvent(reinterpret_cast<uintptr_t>(&hostedSignalHandler),
2063 HostedSignalNumber, ~0UL, 0, true, true);
2064 if (!thread->sendEvent(event)) {
2065 delete event;
2066 return check(false, "a prequeued signal could not be published");
2067 }
2068
2069 const bool delayed = Time::delay(5 * Time::Multiplier::Second);
2070 const Thread::InterruptionReason reason = thread->getInterruptionReason();
2071 thread->clearInterruption();
2072
2073 return check(!delayed && reason == Thread::InterruptedBySignal && g_SignalHandlerCalls == 1,
2074 "a prequeued signal did not interrupt Time::delay");
2075}
2076} // namespace
2077
2078bool runHostedSignalInterruptionRegressions(Thread* thread) {
2079 const bool passed =
2080 eventHandlerPrivilege() && signalCullPreservesNumberCollision(thread) &&
2081 pendingSignalRunsAtSyscallReturn(thread) &&
2082 exactUserReturnSignalDefersWithoutContext(thread) && execPreservesNestedSignalMask(thread) &&
2083 invalidUserHandlerDeliveryFailsClosed(thread) &&
2084#if !defined(PEDIGREE_HOSTED_CORE_SMOKE)
2085 ignoredSignalDoesNotInterruptWait(thread->getParent()) &&
2086 ignoredDispositionDiscardsPendingSignals(thread->getParent()) &&
2087 execSignalResetRebindsPending(thread->getParent()) &&
2088 defaultStopControl(thread->getParent()) &&
2089 staleDefaultStopRejectedAfterContinue(thread->getParent()) &&
2090 staleDefaultStopRejectedAcrossAba(thread->getParent()) &&
2091 execSignalResetRestampsPendingStop(thread->getParent()) &&
2092 signalContinueStillResumes(thread->getParent()) &&
2093 opposingJobControlSignalsCancelAcrossThreads(thread->getParent()) &&
2094 stoppedProcessDefersSignalsUntilContinue(thread->getParent()) &&
2095#endif
2096 temporarySignalMaskNestedPrequeued(thread) && temporarySignalMaskAcrossMutex(thread) &&
2097 temporarySignalMaskIgnoresDefaultAction(thread) &&
2098 conditionVariableSignalInterruption(thread) && bufferSignalInterruption(thread) &&
2099 semaphoreSignalInterruption(thread) && semaphoreSignalAfterOrdinaryWake() &&
2100 conditionSignalAfterOrdinaryWake() && completionSemaphoreSignalDeferral(thread) &&
2101 ringBufferSignalInterruption(thread) && ringBufferMonitorCull(thread) &&
2102 ringBufferMonitorRetirement(thread) && ringBufferMonitorDestructor(thread) &&
2103 delaySignalInterruption(thread) && prequeuedDelaySignalInterruption(thread);
2104 if (passed) {
2105 NOTICE("HOSTED-WAIT-TEST: PASS signal-interruption");
2106 }
2107 return passed;
2108}
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
Definition Event.h:49
virtual bool prefersAlternateUserStack() const
Definition Event.h:249
HandlerPrivilege getHandlerPrivilege() const
Definition Event.h:236
virtual UserReturnDelivery deliverAtUserReturn(InterruptState &)
Definition Event.h:259
virtual bool requiresExactUserReturnState() const
Definition Event.h:254
virtual size_t getNumber()=0
virtual size_t serialize(uint8_t *pBuffer)=0
virtual Event * cloneForDelivery()
Definition Event.h:277
Definition Mutex.h:56
void checkEventState(uintptr_t userStack)
MUST_USE_RESULT bool serviceUserReturnWork(InterruptState &state, UserReturnFrame::Origin origin=UserReturnFrame::Origin::Interrupt, bool diagnosticSample=false)
virtual void sendSignal(Thread *pThread, int signal, bool yield=true, bool processDirected=false)
SignalDeliveryResult queueSignalDelivery(Thread *target, size_t sig, uint32_t *flags=nullptr, int32_t signalCode=0, bool processDirected=false, uint64_t signalValue=0, const SharedPointer< SignalEventState > &state=SharedPointer< SignalEventState >())
void setSignalHandler(size_t sig, SignalHandler *handler)
bool getSignalDisposition(size_t sig, SignalDisposition &disposition, bool beginDelivery=false)
Process * getParent()
Definition Process.h:568
WaitQueue::Guard acquireChildStateWait()
Definition Process.h:682
bool takePendingChildTransition(bool includeStopped, bool includeContinued, ChildTransition &transition)
Definition Process.cc:2046
void publish()
Definition Process.cc:832
void resume()
Definition Process.cc:1968
size_t getContinuationEpoch()
Definition Process.cc:1963
static bool getInterrupts()
static ProcessorInformation & information()
Utility class to provide a ring buffer.
Definition RingBuffer.h:62
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:226
bool hasActiveTemporarySignalMask()
Definition Thread.cc:2172
void setUnwindState(UnwindType ut)
Definition Thread.cc:3628
@ TerminateThread
Exit only this thread during Process exit.
Definition Thread.h:515
bool hasSignalEvent(size_t signalNumber, int processDirected=-1)
Definition Thread.cc:2366
uint64_t getSignalMask()
Definition Thread.cc:2002
bool getWaitDebugInfo(WaitDebugInfo &info)
Definition Thread.cc:3184
bool hasEvent(Event *pEvent)
Definition Thread.cc:2639
void waitForEvent(WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
Definition Thread.cc:1245
bool joinForCompletion()
Definition Thread.cc:2771
bool join()
Definition Thread.cc:2767
bool eventsDeferred() const
Definition Thread.cc:3180
void cullEvent(Event *pEvent)
Definition Thread.cc:2193
DebugState getDebugState(uintptr_t &address)
Definition Thread.h:570
Process * getParent() const
Definition Thread.h:338
void prepareSignalStateForExec()
Definition Thread.cc:2061
void cullSignalEvent(size_t signalNumber)
Definition Thread.cc:2237
void setSignalMask(uint64_t mask)
Definition Thread.cc:2007
class PerProcessorScheduler * getScheduler() const
Definition Thread.h:925
bool hasTemporarySignalWaitInterruption()
Definition Thread.cc:2165
SchedulerState * pushState()
Definition Thread.cc:884
bool start()
Definition Thread.cc:794
bool sendEvent(Event *pEvent)
Definition Thread.cc:1158
size_t getStateLevel() const
Definition Thread.h:314
void abandonCurrentState(bool clean=false)
Definition Thread.cc:999
Definition User.h:32
int type
Type - 0 = normal, 1 = SIG_DFL, 2 = SIG_IGN.
SignalEvent * pEvent
Event for the signal handler.
uintptr_t restorer
Userspace restorer for Linux-compatible signal delivery.
uint64_t sigMask
Signal mask to set when this signal handler is called.
uint32_t flags
Signal handler flags.