From ac82f3c6a9d8ecf083ed20a8f285afbc38ead4f6 Mon Sep 17 00:00:00 2001 From: Hussein Kadhim Date: Tue, 13 Oct 2026 18:19:05 +0300 Subject: [PATCH] fix(libkernel): switch the host stack bounds in siglongjmp on Windows longjmp moves the Windows stack bounds onto the stack of the target jump buffer so that exceptions and stack probes keep working after a jump between guest stacks. siglongjmp restored the same registers without that step, so a jump from a context stack back to the thread stack left the thread bounds describing the stack it had left. --- .../prx/libkernel/System/src/SignalSets.cpp | 10 ++++++++ core/libs/tests/GuestUserContext.cpp | 24 +++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/SignalSets.cpp b/core/libs/prx/libkernel/System/src/SignalSets.cpp index 871866cb3..3f2cf5b43 100644 --- a/core/libs/prx/libkernel/System/src/SignalSets.cpp +++ b/core/libs/prx/libkernel/System/src/SignalSets.cpp @@ -109,6 +109,16 @@ asm(".text\n" " popq %rsi\n" " popq %rdi\n" "2:\n" +#ifdef _WIN32 + " pushq %rdi\n" + " pushq %rsi\n" + " movq 16(%rdi), %rdi\n" + " subq $8, %rsp\n" + " call GuestStackSwitch_nid_no_patch\n" + " addq $8, %rsp\n" + " popq %rsi\n" + " popq %rdi\n" +#endif " movq %rdi, %rdx\n" " movl %esi, %eax\n" " movq 0(%rdx), %rcx\n" diff --git a/core/libs/tests/GuestUserContext.cpp b/core/libs/tests/GuestUserContext.cpp index 41a4b5e0b..cd05dca09 100644 --- a/core/libs/tests/GuestUserContext.cpp +++ b/core/libs/tests/GuestUserContext.cpp @@ -21,6 +21,8 @@ int APS5_VABI setcontext_nid_postfix(const UserUcontext* context); void APS5_VABI makecontext_nid_postfix(UserUcontext* context, ContextEntry entry, int count, ...); [[gnu::returns_twice]] int APS5_VABI setjmp_nid_postfix(std::uint64_t* buffer); [[noreturn]] void APS5_VABI longjmp_nid_postfix(std::uint64_t* buffer, int value); +[[gnu::returns_twice]] int APS5_VABI sigsetjmp_nid_postfix(std::uint64_t* buffer, int saveMask); +[[noreturn]] void APS5_VABI siglongjmp_nid_postfix(std::uint64_t* buffer, int value); int APS5_VABI sigprocmask_nid_postfix(int how, const void* set, void* previousSet); int* APS5_VABI __error_nid_postfix(); @@ -150,6 +152,7 @@ int g_entries = 0; Fibre g_dispatcher = {}; Fibre g_job = {}; int g_jobRuns = 0; +bool g_signalJumps = false; void Require(bool value, const char* what) { if (!value) { @@ -348,6 +351,14 @@ namespace { [[gnu::noinline]] void Switch(Fibre* from, Fibre* to) { from->started = 1; + if (g_signalJumps) { + if (sigsetjmp_nid_postfix(from->jump, 1) == 0) { + if (to->started) siglongjmp_nid_postfix(to->jump, 1); + setcontext_nid_postfix(&to->context); + Require(false, "setcontext into the job returned"); + } + return; + } if (setjmp_nid_postfix(from->jump) == 0) { if (to->started) longjmp_nid_postfix(to->jump, 1); setcontext_nid_postfix(&to->context); @@ -360,7 +371,7 @@ namespace { extern "C" void APS5_VABI JobEntry() { for (;;) { ++g_jobRuns; - CheckStackBounds(&g_jobStack, "the stack bounds follow longjmp onto the job stack"); + CheckStackBounds(&g_jobStack, g_signalJumps ? "the stack bounds follow siglongjmp onto the job stack" : "the stack bounds follow longjmp onto the job stack"); ThrowAndCatch(); Switch(&g_job, &g_dispatcher); } @@ -402,7 +413,11 @@ void CheckMakecontext() { #endif } -void CheckJumpBuffers() { +void CheckJumpBuffers(bool signalJumps) { + g_signalJumps = signalJumps; + g_jobRuns = 0; + g_dispatcher = {}; + g_job = {}; #ifdef _WIN32 ULONG_PTR threadLow = 0; ULONG_PTR threadHigh = 0; @@ -420,7 +435,7 @@ void CheckJumpBuffers() { ULONG_PTR low = 0; ULONG_PTR high = 0; GetCurrentThreadStackLimits(&low, &high); - Require(low == threadLow && high == threadHigh, "the stack bounds follow longjmp back to the thread stack"); + Require(low == threadLow && high == threadHigh, signalJumps ? "the stack bounds follow siglongjmp back to the thread stack" : "the stack bounds follow longjmp back to the thread stack"); #endif } } @@ -466,7 +481,8 @@ int main(int argc, char** argv) { CheckSignalMask(); std::thread([] { CheckMakecontext(); - CheckJumpBuffers(); + CheckJumpBuffers(false); + CheckJumpBuffers(true); GrowStack(32); }).join(); return 0;