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, regs.rax, before.rax);
|
||||||
KT_EQU(t, thread->sigHandlerDepth, 0u);
|
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