test: add more cases for sigTest

This commit is contained in:
dualfroz committed 2026-09-12 14:08:14 +02:00
1 parent 98b0e27b43
commit 978d2ad905
1 file changed
+153
+153
View File
@@ -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, &regs, &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, &regs, &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, &regs, &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, &regs, &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, &regs));
KT_FALSE(t, thread->sigBlocked & (1ull << sigHup));
int finalSignum = -1;
KT_EQ(t, sigConsumePending(thread, &regs, &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, &regs, &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, &regs, &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, &regs));
KT_FALSE(t, thread->sigBlocked & (1ull << sigUsr1));
int finalSignum = -1;
KT_EQ(t, sigConsumePending(thread, &regs, &finalSignum), sigOutcomeHandler);
KT_EQ(t, finalSignum, sigUsr1);
KT_EQU(t, thread->sigHandlerDepth, 1u);
KT_TRUE(t, threadDiscard(id));
}