8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/errors.h"
11#include "pedigree/kernel/process/PerProcessorScheduler.h"
12#include "pedigree/kernel/process/Process.h"
13#include "pedigree/kernel/process/Scheduler.h"
14#include "pedigree/kernel/process/SignalEvent.h"
15#include "pedigree/kernel/process/Thread.h"
16#include "pedigree/kernel/processor/Processor.h"
17#include "pedigree/kernel/processor/VirtualAddressSpace.h"
18#include "pedigree/kernel/time/Time.h"
23#include "modules/subsys/posix/PosixSubsystem.h"
24#include "modules/subsys/posix/signal-syscalls.h"
27constexpr size_t TestSignal = 10;
28constexpr int PreservedErrno = 123;
29constexpr uint64_t TestSignalBit =
static_cast<uint64_t
>(1) << (TestSignal - 1);
30constexpr uint64_t PreservedSignalBit =
static_cast<uint64_t
>(1) << (12 - 1);
31constexpr uint64_t UnblockableSignalBits =
32 (
static_cast<uint64_t
>(1) << (SIGKILL - 1)) | (
static_cast<uint64_t
>(1) << (SIGSTOP - 1));
33constexpr uint64_t OriginalSignalMask = TestSignalBit | PreservedSignalBit;
34constexpr uint64_t RequestedSignalMask = PreservedSignalBit | UnblockableSignalBits;
35constexpr uint64_t ActiveSignalMask = PreservedSignalBit;
45Thread* g_SigsuspendPreEnrolmentTarget =
nullptr;
47void sigsuspendSignalHandler(
size_t) {
49 g_SigsuspendSignalCalls += 1;
50 g_SigsuspendHandlerMask = thread ? thread->
getSignalMask() : 0;
53void sigsuspendNonSignalHandler(
size_t) {
54 g_SigsuspendNonSignalCalls += 1;
57void sigsuspendDefaultStopHandler(
size_t) {
59 g_SigsuspendDefaultStopCalls += 1;
65void sigsuspendDefaultContinueHandler(
size_t) {
67 g_SigsuspendDefaultContinueCalls += 1;
73void sigsuspendPreEnrolmentHook(
Thread* thread) {
74 if (g_SigsuspendPreEnrolmentCalls) {
77 g_SigsuspendPreEnrolmentCalls += 1;
79 SignalEvent signal(
reinterpret_cast<uintptr_t
>(&sigsuspendSignalHandler), TestSignal, ~0UL, 0,
80 true,
false, Event::HandlerPrivilege::Kernel,
81 SignalEvent::DeliveryDisposition::CaughtHandler);
83 g_SigsuspendPreEnrolmentQueued += 1;
93 if (thread == g_SigsuspendPreEnrolmentTarget && debugState == Thread::EventWait) {
94 g_SigsuspendUnexpectedWaits += 1;
99class SigsuspendNonSignalEvent :
public Event {
101 SigsuspendNonSignalEvent()
102 :
Event(reinterpret_cast<uintptr_t>(&sigsuspendNonSignalHandler), false) {}
113bool waitForEventBlock(
Thread* thread) {
114 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
115 while (Time::getTicks() < deadline) {
117 uintptr_t debugAddress = 0;
128 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
129 while (Time::getTicks() < deadline) {
130 if (counter == value) {
135 return counter == value;
138bool waitForSuspensionState(
Process* process,
bool suspended) {
139 const Time::Timestamp deadline = Time::getTicks() + (2 * Time::Multiplier::Second);
140 while (Time::getTicks() < deadline) {
141 if (process->isSuspended() == suspended) {
146 return process->isSuspended() == suspended;
149struct SigsuspendValidationContext {
150 SigsuspendValidationContext() : entered(0), returned(0), passed(false) {}
157int sigsuspendValidationWorker(
void* parameter) {
158 SigsuspendValidationContext* context =
reinterpret_cast<SigsuspendValidationContext*
>(parameter);
160 context->entered += 1;
162 thread->clearInterruption();
163 g_SigsuspendSignalCalls = 0;
164 g_SigsuspendHandlerMask = 0;
166 SignalEvent pendingSignal(
reinterpret_cast<uintptr_t
>(&sigsuspendSignalHandler), TestSignal);
167 const bool signalQueued = thread->
sendEvent(&pendingSignal);
171 const int wrongSize =
172 posix_rt_sigsuspend(
reinterpret_cast<const uint64_t*
>(kernelStart),
sizeof(uint64_t) - 1);
173 const int wrongSizeError = thread->
getErrno();
174 const bool wrongSizePreserved = thread->
getSignalMask() == OriginalSignalMask &&
175 thread->
hasEvent(&pendingSignal) && !g_SigsuspendSignalCalls;
178 const int badAddress =
179 posix_rt_sigsuspend(
reinterpret_cast<const uint64_t*
>(kernelStart),
sizeof(uint64_t));
180 const int badAddressError = thread->
getErrno();
181 const bool badAddressPreserved = thread->
getSignalMask() == OriginalSignalMask &&
182 thread->
hasEvent(&pendingSignal) && !g_SigsuspendSignalCalls;
185 const int nullAddress = posix_rt_sigsuspend(
nullptr,
sizeof(uint64_t));
186 const int nullAddressError = thread->
getErrno();
187 const bool nullAddressPreserved = thread->
getSignalMask() == OriginalSignalMask &&
188 thread->
hasEvent(&pendingSignal) && !g_SigsuspendSignalCalls;
191 const int result = posix_rt_sigsuspend(&RequestedSignalMask,
sizeof(RequestedSignalMask));
192 const int error = thread->
getErrno();
194 const bool signalConsumed = !thread->
hasEvent(&pendingSignal);
195 const bool interruptionConsumed = thread->getInterruptionReason() == Thread::NotInterrupted;
197 if (thread->
hasEvent(&pendingSignal)) {
201 thread->clearInterruption();
204 signalQueued && wrongSize == -1 && wrongSizeError == Error::InvalidArgument &&
205 wrongSizePreserved && badAddress == -1 && badAddressError == Error::BadAddress &&
206 badAddressPreserved && nullAddress == -1 && nullAddressError == Error::BadAddress &&
207 nullAddressPreserved && result == -1 && error == Error::Interrupted &&
208 restoredMask == OriginalSignalMask && signalConsumed && interruptionConsumed &&
209 g_SigsuspendSignalCalls == 1 && g_SigsuspendHandlerMask == (ActiveSignalMask | TestSignalBit);
210 context->returned += 1;
211 return context->passed ? 0 : 1;
214bool sigsuspendValidationAndPrequeued(
Process* kernelProcess) {
217 SigsuspendValidationContext context;
219 new Thread(process, sigsuspendValidationWorker, &context,
nullptr,
false,
true,
true);
220 worker->setName(
"hosted rt_sigsuspend validation worker");
221 const bool started =
worker->start();
222 const bool entered = started && waitForCounter(context.entered, 1);
223 const bool returned = entered && waitForCounter(context.returned, 1);
224 if (started && !context.returned) {
227 const bool joined = started &&
worker->joinForCompletion();
233 started && entered && returned && joined && context.returned == 1 && context.passed;
237 "HOSTED-SYSCALL-TEST: FAIL rt-sigsuspend-validation: "
238 "Linux sigset validation, prequeued delivery, or restoration regressed");
242 NOTICE(
"HOSTED-SYSCALL-TEST: PASS rt-sigsuspend-validation");
246struct SigsuspendWaitContext {
247 SigsuspendWaitContext() : entered(0), returned(0), result(-2), error(0), restoredMask(0) {}
253 uint64_t restoredMask;
256int sigsuspendWaitWorker(
void* parameter) {
257 SigsuspendWaitContext* context =
reinterpret_cast<SigsuspendWaitContext*
>(parameter);
260 thread->clearInterruption();
261 context->entered += 1;
263 context->result = posix_rt_sigsuspend(&RequestedSignalMask,
sizeof(RequestedSignalMask));
264 context->error = thread->
getErrno();
267 thread->clearInterruption();
268 context->returned += 1;
272bool sigsuspendClosesPreEnrolmentWindow(
Process* kernelProcess) {
275 g_SigsuspendSignalCalls = 0;
276 g_SigsuspendHandlerMask = 0;
277 g_SigsuspendPreEnrolmentCalls = 0;
278 g_SigsuspendPreEnrolmentQueued = 0;
279 g_SigsuspendUnexpectedWaits = 0;
281 SigsuspendWaitContext context;
282 Thread*
worker =
new Thread(process, sigsuspendWaitWorker, &context,
nullptr,
false,
true,
true);
283 worker->setName(
"hosted rt_sigsuspend pre-enrolment worker");
284 g_SigsuspendPreEnrolmentTarget =
worker;
285 Thread::setSignalWaitPreEnrolmentHookForHostedTest(
worker, sigsuspendPreEnrolmentHook);
286 WaitQueue::setBeforeBlockHook(sigsuspendUnexpectedWaitHook);
288 const bool started =
worker->start();
289 const bool returned = started && waitForCounter(context.returned, 1);
291 Thread::setSignalWaitPreEnrolmentHookForHostedTest(
nullptr,
nullptr);
292 WaitQueue::setBeforeBlockHook(
nullptr);
293 g_SigsuspendPreEnrolmentTarget =
nullptr;
294 if (started && !context.returned) {
297 const bool joined = started &&
worker->joinForCompletion();
302 const bool passed = started && returned && joined && context.result == -1 &&
303 context.error == Error::Interrupted &&
304 context.restoredMask == OriginalSignalMask &&
305 g_SigsuspendPreEnrolmentCalls == 1 && g_SigsuspendPreEnrolmentQueued == 1 &&
306 g_SigsuspendUnexpectedWaits == 0 && g_SigsuspendSignalCalls == 1 &&
307 g_SigsuspendHandlerMask == (ActiveSignalMask | TestSignalBit);
311 "HOSTED-SYSCALL-TEST: FAIL rt-sigsuspend-pre-enrolment: "
312 "a caught signal was lost between predicate inspection and wait publication");
316 NOTICE(
"HOSTED-SYSCALL-TEST: PASS rt-sigsuspend-pre-enrolment");
320void installDefaultSignalAction(
PosixSubsystem* subsystem,
size_t signal, uintptr_t handler) {
322 disposition->
type = 1;
324 new SignalEvent(handler, signal, ~0UL, 0,
true,
false, Event::HandlerPrivilege::Kernel,
325 SignalEvent::DeliveryDisposition::DefaultAction);
329bool sigsuspendContinuesAfterDefaultActions(
Process* kernelProcess) {
332 process->setSubsystem(subsystem);
333 installDefaultSignalAction(subsystem, SIGCHLD,
334 reinterpret_cast<uintptr_t
>(&sigsuspendNonSignalHandler));
335 installDefaultSignalAction(subsystem, SIGTSTP,
336 reinterpret_cast<uintptr_t
>(&sigsuspendDefaultStopHandler));
337 installDefaultSignalAction(subsystem, SIGCONT,
338 reinterpret_cast<uintptr_t
>(&sigsuspendDefaultContinueHandler));
340 g_SigsuspendSignalCalls = 0;
341 g_SigsuspendHandlerMask = 0;
342 g_SigsuspendNonSignalCalls = 0;
343 g_SigsuspendDefaultStopCalls = 0;
344 g_SigsuspendDefaultContinueCalls = 0;
345 SigsuspendWaitContext context;
346 Thread*
worker =
new Thread(process, sigsuspendWaitWorker, &context,
nullptr,
false,
true,
true);
347 worker->setName(
"hosted rt_sigsuspend default-action worker");
349 const bool started =
worker->start();
350 const bool entered = started && waitForCounter(context.entered, 1);
351 const bool initiallyBlocked = entered && waitForEventBlock(
worker);
352 const PosixSubsystem::SignalDeliveryResult ignoredResult =
354 : PosixSubsystem::SignalDeliveryResult::Unavailable;
355 const bool ignoredStayedBlocked =
356 ignoredResult == PosixSubsystem::SignalDeliveryResult::Ignored && !context.returned &&
357 waitForEventBlock(
worker) && !g_SigsuspendNonSignalCalls;
359 const PosixSubsystem::SignalDeliveryResult stopResult =
361 : PosixSubsystem::SignalDeliveryResult::Unavailable;
362 const bool stopped = stopResult == PosixSubsystem::SignalDeliveryResult::Queued &&
363 waitForSuspensionState(process,
true) &&
364 waitForCounter(g_SigsuspendDefaultStopCalls, 1) && !context.returned;
366 if (process->isSuspended()) {
369 const PosixSubsystem::SignalDeliveryResult continueResult =
371 : PosixSubsystem::SignalDeliveryResult::Unavailable;
372 const bool continued = waitForSuspensionState(process,
false);
373 const bool defaultHandlersReturned =
374 continueResult == PosixSubsystem::SignalDeliveryResult::Queued && continued &&
375 waitForCounter(g_SigsuspendDefaultContinueCalls, 1);
376 const bool blockedAgain =
377 defaultHandlersReturned && !context.returned && waitForEventBlock(
worker);
379 SignalEvent caughtSignal(
reinterpret_cast<uintptr_t
>(&sigsuspendSignalHandler), TestSignal, ~0UL,
380 0,
true,
false, Event::HandlerPrivilege::Kernel,
381 SignalEvent::DeliveryDisposition::CaughtHandler);
382 const bool caughtQueued = blockedAgain &&
worker->sendEvent(&caughtSignal);
383 const bool returned = caughtQueued && waitForCounter(context.returned, 1);
384 if (process->isSuspended()) {
387 if (started && !context.returned) {
391 const bool joined = started &&
worker->joinForCompletion();
397 const bool passed = started && initiallyBlocked && ignoredStayedBlocked && stopped &&
398 defaultHandlersReturned && blockedAgain && caughtQueued && returned &&
399 joined && context.result == -1 && context.error == Error::Interrupted &&
400 context.restoredMask == OriginalSignalMask &&
401 g_SigsuspendDefaultStopCalls == 1 && g_SigsuspendDefaultContinueCalls == 1 &&
402 g_SigsuspendSignalCalls == 1;
406 "HOSTED-SYSCALL-TEST: FAIL rt-sigsuspend-default-actions: "
407 "an ignored, stop, or continue default action ended the signal wait");
411 NOTICE(
"HOSTED-SYSCALL-TEST: PASS rt-sigsuspend-default-actions");
415bool sigsuspendIgnoresNonSignalWake(
Process* kernelProcess) {
418 g_SigsuspendSignalCalls = 0;
419 g_SigsuspendNonSignalCalls = 0;
420 g_SigsuspendHandlerMask = 0;
422 SigsuspendWaitContext context;
423 Thread*
worker =
new Thread(process, sigsuspendWaitWorker, &context,
nullptr,
false,
true,
true);
424 worker->setName(
"hosted rt_sigsuspend non-signal wake worker");
425 SigsuspendNonSignalEvent nonSignal;
426 SignalEvent signal(
reinterpret_cast<uintptr_t
>(&sigsuspendSignalHandler), TestSignal);
428 const bool started =
worker->start();
429 const bool entered = started && waitForCounter(context.entered, 1);
430 const bool initiallyBlocked = entered && waitForEventBlock(
worker);
431 const bool activeMaskObserved = initiallyBlocked &&
worker->getSignalMask() == ActiveSignalMask;
432 const bool nonSignalQueued = initiallyBlocked &&
worker->sendEvent(&nonSignal);
433 const bool nonSignalHandled = nonSignalQueued && waitForCounter(g_SigsuspendNonSignalCalls, 1);
434 const bool stayedInCall = nonSignalHandled && !context.returned;
435 const bool blockedAgain = stayedInCall && waitForEventBlock(
worker);
436 const bool signalQueued = blockedAgain &&
worker->sendEvent(&signal);
438 const Time::Timestamp returnDeadline = Time::getTicks() + (2 * Time::Multiplier::Second);
439 while (started && !context.returned && Time::getTicks() < returnDeadline) {
442 if (started && !context.returned) {
445 const bool joined = started &&
worker->joinForCompletion();
451 started && initiallyBlocked && activeMaskObserved && nonSignalQueued && nonSignalHandled &&
452 stayedInCall && blockedAgain && signalQueued && joined && context.returned == 1 &&
453 context.result == -1 && context.error == Error::Interrupted &&
454 context.restoredMask == OriginalSignalMask && g_SigsuspendNonSignalCalls == 1 &&
455 g_SigsuspendSignalCalls == 1 && g_SigsuspendHandlerMask == (ActiveSignalMask | TestSignalBit);
459 "HOSTED-SYSCALL-TEST: FAIL rt-sigsuspend-nonsignal: "
460 "a non-signal wake returned early or signal restoration regressed");
464 NOTICE(
"HOSTED-SYSCALL-TEST: PASS rt-sigsuspend-nonsignal");
469bool runHostedRtSigsuspendRegressions(
Process* process) {
470 return sigsuspendValidationAndPrequeued(process) && sigsuspendClosesPreEnrolmentWindow(process) &&
471 sigsuspendContinuesAfterDefaultActions(process) && sigsuspendIgnoresNonSignalWake(process);
virtual size_t getNumber()=0
virtual size_t serialize(uint8_t *pBuffer)=0
void checkEventState(uintptr_t userStack)
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)
void suspend(int stopSignal=0)
static ProcessorInformation & information()
static Scheduler & instance()
void setErrno(size_t err)
void setUnwindState(UnwindType ut)
@ TerminateThread
Exit only this thread during Process exit.
bool getWaitDebugInfo(WaitDebugInfo &info)
bool hasEvent(Event *pEvent)
void cullEvent(Event *pEvent)
DebugState getDebugState(uintptr_t &address)
Process * getParent() const
void setSignalMask(uint64_t mask)
class PerProcessorScheduler * getScheduler() const
bool sendEvent(Event *pEvent)
int type
Type - 0 = normal, 1 = SIG_DFL, 2 = SIG_IGN.
SignalEvent * pEvent
Event for the signal handler.