test: add more cases for sigTest
This commit is contained in:
1 parent
98b0e27b43
commit
978d2ad905
1 file changed
+153
+153
@@ -473,3 +473,156 @@ KTEST(sigConsumePendingDefaultDispositionReturnsTerminate)
|
||||
KT_EQU(t, regs.rax, before.rax);
|
||||
KT_EQU(t, thread->sigHandlerDepth, 0u);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
KTEST(sigConsumePendingSigTstpDefaultDispositionReturnsStopOutcome)
|
||||
{
|
||||
uint32 id = threadCreate("sigConsumeTstp", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
Thread* thread = threadAt(id);
|
||||
Registers regs = makeUserRegs();
|
||||
Registers before = regs;
|
||||
int signum = -1;
|
||||
|
||||
/* SIGTSTP's real-world default disposition is Stop, not Terminate --
|
||||
the catchable half of job control (see sigDefaultIsStop, signal.c).
|
||||
sigStop itself never reaches here at all (sigRaise bypasses this
|
||||
whole scan for it, see sigRaiseSigStopDelegatesToThreadStopAndBypassesPending
|
||||
above), so this is the ONLY way sigOutcomeStop is ever produced. */
|
||||
sigRaise(id, sigTstp);
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeStop);
|
||||
KT_EQ(t, signum, sigTstp);
|
||||
KT_FALSE(t, thread->sigPending & (1ull << sigTstp));
|
||||
|
||||
/* Reported, not carried out, here -- isr.c's deliverPendingSignals is
|
||||
the one that actually calls threadStop, the same contract
|
||||
sigOutcomeTerminate already has with threadExit. */
|
||||
KT_EQU(t, regs.rip, before.rip);
|
||||
KT_EQU(t, regs.rax, before.rax);
|
||||
KT_EQU(t, thread->sigHandlerDepth, 0u);
|
||||
KT_EQ(t, thread->state, threadRunnable);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
KTEST(sigConsumePendingSigTstpWithHandlerRunsHandlerInsteadOfStopping)
|
||||
{
|
||||
uint32 id = threadCreate("sigConsumeTstpCaught", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
Thread* thread = threadAt(id);
|
||||
Registers regs = makeUserRegs();
|
||||
int signum = -1;
|
||||
|
||||
/* Unlike SIGSTOP, SIGTSTP is fully catchable: an installed handler
|
||||
overrides the default Stop disposition, exactly like any other
|
||||
catchable signal overriding a default Terminate. */
|
||||
uint64 handlerAddr = 0x4000A000ull;
|
||||
sigSetHandler(thread, sigTstp, handlerAddr, 0, 0);
|
||||
sigRaise(id, sigTstp);
|
||||
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||
KT_EQ(t, signum, sigTstp);
|
||||
KT_EQU(t, regs.rax, handlerAddr);
|
||||
KT_EQU(t, regs.rip, sigTrampolineVirtualAddr);
|
||||
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
KTEST(sigConsumePendingSaMaskDefersDifferentSignalUntilHandlerReturns)
|
||||
{
|
||||
uint32 id = threadCreate("sigSaMaskTarget", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
Thread* thread = threadAt(id);
|
||||
Registers regs = makeUserRegs();
|
||||
int signum = -1;
|
||||
|
||||
/* sigUsr1's sa_mask explicitly includes sigHup -- unlike the nesting
|
||||
test below (mask 0), THIS is what should keep sigHup from
|
||||
interrupting sigUsr1's handler at all. */
|
||||
uint64 usr1Handler = 0x40009000ull;
|
||||
uint64 hupHandler = 0x4000C000ull;
|
||||
sigSetHandler(thread, sigUsr1, usr1Handler, (1ull << sigHup), 0);
|
||||
sigSetHandler(thread, sigHup, hupHandler, 0, 0);
|
||||
|
||||
sigRaise(id, sigUsr1);
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||
KT_EQ(t, signum, sigUsr1);
|
||||
KT_TRUE(t, thread->sigBlocked & (1ull << sigHup));
|
||||
|
||||
/* sigHup arrives while sigUsr1's handler is active -- masked by its
|
||||
sa_mask, so it must stay pending, NOT nest, unlike an unmasked
|
||||
signal would (see the nesting test below). */
|
||||
sigRaise(id, sigHup);
|
||||
int deferredSignum = -1;
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &deferredSignum), sigOutcomeNone);
|
||||
KT_EQ(t, deferredSignum, -1);
|
||||
KT_TRUE(t, thread->sigPending & (1ull << sigHup));
|
||||
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||
|
||||
/* Once sigUsr1's handler returns, its sa_mask's grip on sigHup ends
|
||||
with it (sigBlocked restored to what it was before) -- the deferred
|
||||
signal is finally deliverable, as its own TOP-LEVEL handler, not a
|
||||
nested one. */
|
||||
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||
KT_FALSE(t, thread->sigBlocked & (1ull << sigHup));
|
||||
|
||||
int finalSignum = -1;
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &finalSignum), sigOutcomeHandler);
|
||||
KT_EQ(t, finalSignum, sigHup);
|
||||
KT_EQU(t, regs.rax, hupHandler);
|
||||
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
/* Default (sigFlagNoDefer NOT set) half of the contrast: a signal raised
|
||||
again against ITSELF while its own handler is still active must stay
|
||||
merely pending, not nest -- sigConsumePendingCustomHandlerRewritesTrapframeAndPushesContext
|
||||
above already asserts sigUsr1's own bit lands in sigBlocked once its
|
||||
handler starts; this test is the direct behavioral consequence of that
|
||||
bit actually being there, driven the same way
|
||||
sigConsumePendingSaMaskDefersDifferentSignalUntilHandlerReturns drives
|
||||
sa_mask's deferral. */
|
||||
KTEST(sigConsumePendingDefersSameSignalRaisedAgainWhileItsOwnHandlerIsActive)
|
||||
{
|
||||
uint32 id = threadCreate("sigNoDeferOffTarget", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
Thread* thread = threadAt(id);
|
||||
Registers regs = makeUserRegs();
|
||||
int signum = -1;
|
||||
|
||||
uint64 handlerAddr = 0x40009000ull;
|
||||
sigSetHandler(thread, sigUsr1, handlerAddr, 0, 0);
|
||||
sigRaise(id, sigUsr1);
|
||||
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||
KT_EQ(t, signum, sigUsr1);
|
||||
KT_TRUE(t, thread->sigBlocked & (1ull << sigUsr1));
|
||||
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||
|
||||
/* Same signum, raised again while its own handler is still active --
|
||||
self-blocked (no sigFlagNoDefer), so this must stay pending, not
|
||||
enter a second, nested instance of the same handler. */
|
||||
sigRaise(id, sigUsr1);
|
||||
int deferredSignum = -1;
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &deferredSignum), sigOutcomeNone);
|
||||
KT_EQ(t, deferredSignum, -1);
|
||||
KT_TRUE(t, thread->sigPending & (1ull << sigUsr1));
|
||||
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||
|
||||
/* Once the (only) active handler returns, its self-block lifts and the
|
||||
deferred re-raise is finally deliverable -- as a fresh TOP-LEVEL
|
||||
invocation (depth back to 1, not 2), never having nested at all. */
|
||||
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||
KT_FALSE(t, thread->sigBlocked & (1ull << sigUsr1));
|
||||
|
||||
int finalSignum = -1;
|
||||
KT_EQ(t, sigConsumePending(thread, ®s, &finalSignum), sigOutcomeHandler);
|
||||
KT_EQ(t, finalSignum, sigUsr1);
|
||||
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user