test: strengthen tests for sigTest
This commit is contained in:
1 parent
978d2ad905
commit
febf5254a4
1 file changed
+155
+155
@@ -626,3 +626,158 @@ KTEST(sigConsumePendingDefersSameSignalRaisedAgainWhileItsOwnHandlerIsActive)
|
|||||||
KT_TRUE(t, threadDiscard(id));
|
KT_TRUE(t, threadDiscard(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* sigFlagNoDefer's whole point: registered for a signal, that signal's own
|
||||||
|
bit is no longer added to sigBlocked when its handler starts, so raising
|
||||||
|
the SAME signum again while that handler is still active genuinely
|
||||||
|
nests a second instance of it -- the direct opposite outcome of the
|
||||||
|
test just above, same signum, only the flag differs. */
|
||||||
|
KTEST(sigConsumePendingNoDeferFlagLetsSameSignalNestIntoItsOwnActiveHandler)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigNoDeferOnTarget", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
Registers regs = makeUserRegs();
|
||||||
|
int signum = -1;
|
||||||
|
|
||||||
|
uint64 handlerAddr = 0x40009000ull;
|
||||||
|
sigSetHandler(thread, sigUsr1, handlerAddr, 0, sigFlagNoDefer);
|
||||||
|
sigRaise(id, sigUsr1);
|
||||||
|
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||||
|
KT_EQ(t, signum, sigUsr1);
|
||||||
|
/* The one thing sigFlagNoDefer actually changes: sigUsr1's own bit is
|
||||||
|
NOT in sigBlocked, unlike the default case above. */
|
||||||
|
KT_FALSE(t, thread->sigBlocked & (1ull << sigUsr1));
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||||
|
|
||||||
|
Registers midHandler = regs;
|
||||||
|
sigRaise(id, sigUsr1);
|
||||||
|
int nestedSignum = -1;
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &nestedSignum), sigOutcomeHandler);
|
||||||
|
KT_EQ(t, nestedSignum, sigUsr1);
|
||||||
|
KT_EQU(t, regs.rip, sigTrampolineVirtualAddr);
|
||||||
|
KT_EQU(t, regs.rax, handlerAddr);
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 2u);
|
||||||
|
/* Still no self-block even at the nested depth -- a third raise would
|
||||||
|
nest again the same way, bounded only by sigMaxNestDepth, never by
|
||||||
|
sigUsr1 blocking itself. */
|
||||||
|
KT_FALSE(t, thread->sigBlocked & (1ull << sigUsr1));
|
||||||
|
KT_EQ(t, memcmp(&thread->sigSavedRegs[1], &midHandler, sizeof(midHandler)), 0);
|
||||||
|
|
||||||
|
/* Unwinding pops the nested instance first, resuming the outer one
|
||||||
|
exactly where the re-raise interrupted it -- LIFO, same as any other
|
||||||
|
nested delivery. */
|
||||||
|
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||||
|
KT_EQ(t, memcmp(®s, &midHandler, sizeof(midHandler)), 0);
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||||
|
|
||||||
|
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 0u);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigConsumePendingCustomHandlerRewritesTrapframeAndPushesContext)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigConsumeHandler", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
Registers regs = makeUserRegs();
|
||||||
|
Registers before = regs;
|
||||||
|
int signum = -1;
|
||||||
|
|
||||||
|
uint64 handlerAddr = 0x40009000ull;
|
||||||
|
sigSetHandler(thread, sigUsr1, handlerAddr, 0, 0);
|
||||||
|
sigProcMask(thread, sigMaskBlock, (1ull << sigUsr2));
|
||||||
|
uint64 blockedBefore = thread->sigBlocked;
|
||||||
|
sigRaise(id, sigUsr1);
|
||||||
|
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||||
|
KT_EQ(t, signum, sigUsr1);
|
||||||
|
|
||||||
|
/* Trapframe rewritten to enter the trampoline: rip is the shared
|
||||||
|
trampoline entry, rax is the handler address its `call *%rax`
|
||||||
|
invokes, rdi is the signal number (the handler's sole argument). */
|
||||||
|
KT_EQU(t, regs.rip, sigTrampolineVirtualAddr);
|
||||||
|
KT_EQU(t, regs.rax, handlerAddr);
|
||||||
|
KT_EQU(t, regs.rdi, (uint64)sigUsr1);
|
||||||
|
/* Untouched: the trampoline runs on the interrupted thread's own
|
||||||
|
existing stack, no separate signal stack. */
|
||||||
|
KT_EQU(t, regs.rsp, before.rsp);
|
||||||
|
|
||||||
|
/* sigUsr1's own sa_mask is empty (0 above), so the ONLY new bit added
|
||||||
|
to sigBlocked is sigUsr1 itself (self-blocked, like real Unix) --
|
||||||
|
whatever was already blocked (sigUsr2, from sigProcMask) survives
|
||||||
|
untouched, a union not a replacement. */
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||||
|
KT_EQU(t, thread->sigBlocked, blockedBefore | (1ull << sigUsr1));
|
||||||
|
KT_EQU(t, thread->sigSavedBlocked[0], blockedBefore);
|
||||||
|
|
||||||
|
/* The ENTIRE pre-signal trapframe must be recoverable later -- not
|
||||||
|
just the fields this test happens to check above. */
|
||||||
|
KT_EQ(t, memcmp(&thread->sigSavedRegs[0], &before, sizeof(before)), 0);
|
||||||
|
|
||||||
|
/* An ALREADY-blocked signal (sigUsr2, blocked since before sigUsr1 was
|
||||||
|
even raised) stays deferred while a handler is active, exactly like
|
||||||
|
it would with no handler running at all -- the depth stack does not
|
||||||
|
change what sigBlocked itself gates. */
|
||||||
|
sigRaise(id, sigUsr2);
|
||||||
|
int blockedSignum = -1;
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &blockedSignum), sigOutcomeNone);
|
||||||
|
KT_EQ(t, blockedSignum, -1);
|
||||||
|
KT_TRUE(t, thread->sigPending & (1ull << sigUsr2));
|
||||||
|
|
||||||
|
/* A signal NOT covered by sigUsr1's mask nor its own self-block --
|
||||||
|
sigHup -- genuinely interrupts the active handler with a NESTED one
|
||||||
|
of its own: this is the behavior a single "one handler at a time"
|
||||||
|
bool (this code used to have) could never produce, since it gated
|
||||||
|
delivery before sigBlocked was even consulted. */
|
||||||
|
Registers midUsr1Handler = regs;
|
||||||
|
uint64 handlerAddrHup = 0x4000B000ull;
|
||||||
|
sigSetHandler(thread, sigHup, handlerAddrHup, 0, 0);
|
||||||
|
sigRaise(id, sigHup);
|
||||||
|
int nestedSignum = -1;
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &nestedSignum), sigOutcomeHandler);
|
||||||
|
KT_EQ(t, nestedSignum, sigHup);
|
||||||
|
KT_EQU(t, regs.rip, sigTrampolineVirtualAddr);
|
||||||
|
KT_EQU(t, regs.rax, handlerAddrHup);
|
||||||
|
KT_EQU(t, regs.rdi, (uint64)sigHup);
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 2u);
|
||||||
|
/* sigHup's own handler has no mask either, so its blocked set is
|
||||||
|
whatever sigUsr1's handler already made it, plus sigHup's own bit. */
|
||||||
|
KT_EQU(t, thread->sigBlocked,
|
||||||
|
blockedBefore | (1ull << sigUsr1) | (1ull << sigHup));
|
||||||
|
/* What got pushed for THIS (nested) frame is exactly the trapframe
|
||||||
|
sigUsr1's handler was actually running with the instant sigHup
|
||||||
|
preempted it -- not the original pre-sigUsr1 `before` snapshot. */
|
||||||
|
KT_EQ(t, memcmp(&thread->sigSavedRegs[1], &midUsr1Handler,
|
||||||
|
sizeof(midUsr1Handler)), 0);
|
||||||
|
KT_EQU(t, thread->sigSavedBlocked[1], blockedBefore | (1ull << sigUsr1));
|
||||||
|
|
||||||
|
/* Unwinding: first sigOnSigReturn resumes sigUsr1's still-active
|
||||||
|
handler exactly where sigHup interrupted it, not the original
|
||||||
|
program -- LIFO, like a real call stack of signal frames. */
|
||||||
|
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||||
|
KT_EQ(t, memcmp(®s, &midUsr1Handler, sizeof(midUsr1Handler)), 0);
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 1u);
|
||||||
|
KT_EQU(t, thread->sigBlocked, blockedBefore | (1ull << sigUsr1));
|
||||||
|
|
||||||
|
/* Second sigOnSigReturn finally resumes the ORIGINAL interrupted
|
||||||
|
program, restoring the exact pre-sigUsr1 trapframe and blocked
|
||||||
|
mask. */
|
||||||
|
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||||
|
KT_EQ(t, memcmp(®s, &before, sizeof(before)), 0);
|
||||||
|
KT_EQU(t, thread->sigHandlerDepth, 0u);
|
||||||
|
KT_EQU(t, thread->sigBlocked, blockedBefore);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigConsumePendingRefusesNewHandlerAtMaxNestDepth)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigNestDepthLimit", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
Registers regs = makeUserRegs();
|
||||||
|
int signum = -1;
|
||||||
|
|
||||||
Reference in new issue
Block a user