From 2622c0bec38d05adb4d7ab0323b287dbbe946573 Mon Sep 17 00:00:00 2001 From: Austin Wise Date: Sun, 7 Jul 2024 14:28:04 -0700 Subject: [PATCH] Don't use alternate stack on illumos or Solaris. When .NET translates SIGSEV to NullReferenceException, it does not return from the signal handler. Instead it resumes execution at the catch handler for the exception. This is not recommend by the manpage for sigaction(2): > It is not recommended that [the ucontext] arg be used by the handler to > restore the context from before the signal delivery. The practical effect of resuming execution without returning from a handler is that the alternate stack will not be used for subsequent signal delivery. This is in contrast to the behavior on linux, which will always use the alternate stack if the stack pointer at the time of fault does not fall on the alternate stack. Since the alternate stack is only usable for a single exception, don't bother using it for any exceptions. --- src/coreclr/pal/src/exception/signal.cpp | 13 +++++++++++-- src/coreclr/pal/src/include/pal/thread.hpp | 11 ++++++++--- src/coreclr/pal/src/init/sxs.cpp | 2 +- src/coreclr/pal/src/thread/thread.cpp | 6 +++--- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp index 4a5cfac1d41ea5..3d7edb2cf8147d 100644 --- a/src/coreclr/pal/src/exception/signal.cpp +++ b/src/coreclr/pal/src/exception/signal.cpp @@ -190,13 +190,20 @@ BOOL SEHInitializeSignals(CorUnix::CPalThread *pthrCurrent, DWORD flags) handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv); #else handle_signal(SIGTRAP, sigtrap_handler, &g_previous_sigtrap); + int additionalFlagsForSigSegv = 0; +#ifndef TARGET_SUNOS + // On platforms that support it, // SIGSEGV handler runs on a separate stack so that we can handle stack overflow - handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv, SA_ONSTACK); + additionalFlagsForSigSegv |= SA_ONSTACK; +#endif + handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv, additionalFlagsForSigSegv); +#ifndef TARGET_SUNOS if (!pthrCurrent->EnsureSignalAlternateStack()) { return FALSE; } +#endif // Allocate the minimal stack necessary for handling stack overflow int stackOverflowStackSize = ALIGN_UP(sizeof(SignalHandlerWorkerReturnPoint), 16) + 7 * 4096; @@ -344,7 +351,7 @@ Return : --*/ bool IsRunningOnAlternateStack(void *context) { -#if HAVE_MACH_EXCEPTIONS +#if HAVE_MACH_EXCEPTIONS || defined(TARGET_SUNOS) return false; #else bool isRunningOnAlternateStack; @@ -656,6 +663,7 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) // Now that we know the SIGSEGV didn't happen due to a stack overflow, execute the common // hardware signal handler on the original stack. +#ifndef TARGET_SUNOS if (GetCurrentPalThread() && IsRunningOnAlternateStack(context)) { if (SwitchStackAndExecuteHandler(code, siginfo, context, 0 /* sp */)) // sp == 0 indicates execution on the original stack @@ -664,6 +672,7 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) } } else +#endif { // The code flow gets here when the signal handler is not running on an alternate stack or when it wasn't created // by coreclr. In both cases, we execute the common_signal_handler directly. diff --git a/src/coreclr/pal/src/include/pal/thread.hpp b/src/coreclr/pal/src/include/pal/thread.hpp index 411040f39b21a5..e45d7d8faa0b79 100644 --- a/src/coreclr/pal/src/include/pal/thread.hpp +++ b/src/coreclr/pal/src/include/pal/thread.hpp @@ -288,8 +288,11 @@ namespace CorUnix void* m_stackBase; // Limit address of the stack of this thread void* m_stackLimit; + +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) // Signal handler's alternate stack to help with stack overflow void* m_alternateStack; +#endif // // The thread entry routine (called from InternalCreateThread) @@ -343,8 +346,10 @@ namespace CorUnix m_fStartStatus(FALSE), m_fStartStatusSet(FALSE), m_stackBase(NULL), - m_stackLimit(NULL), - m_alternateStack(NULL) + m_stackLimit(NULL) +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) + ,m_alternateStack(NULL) +#endif { }; @@ -588,7 +593,7 @@ namespace CorUnix m_pNext = pNext; }; -#if !HAVE_MACH_EXCEPTIONS +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) BOOL EnsureSignalAlternateStack( void diff --git a/src/coreclr/pal/src/init/sxs.cpp b/src/coreclr/pal/src/init/sxs.cpp index 4c1772896a99ca..b7e17c106c4a6d 100644 --- a/src/coreclr/pal/src/init/sxs.cpp +++ b/src/coreclr/pal/src/init/sxs.cpp @@ -64,7 +64,7 @@ AllocatePalThread(CPalThread **ppThread) goto exit; } -#if !HAVE_MACH_EXCEPTIONS +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) // Ensure alternate stack for SIGSEGV handling. Our SIGSEGV handler is set to // run on an alternate stack and the stack needs to be allocated per thread. if (!pThread->EnsureSignalAlternateStack()) diff --git a/src/coreclr/pal/src/thread/thread.cpp b/src/coreclr/pal/src/thread/thread.cpp index abfde0de726f32..97b7c180d7af2a 100644 --- a/src/coreclr/pal/src/thread/thread.cpp +++ b/src/coreclr/pal/src/thread/thread.cpp @@ -157,7 +157,7 @@ static void InternalEndCurrentThreadWrapper(void *arg) will lock its own critical section */ LOADCallDllMain(DLL_THREAD_DETACH, NULL); -#if !HAVE_MACH_EXCEPTIONS +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) pThread->FreeSignalAlternateStack(); #endif // !HAVE_MACH_EXCEPTIONS @@ -1667,7 +1667,7 @@ CPalThread::ThreadEntry( } #endif // HAVE_SCHED_GETAFFINITY && HAVE_SCHED_SETAFFINITY -#if !HAVE_MACH_EXCEPTIONS +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) if (!pThread->EnsureSignalAlternateStack()) { ASSERT("Cannot allocate alternate stack for SIGSEGV!\n"); @@ -2390,7 +2390,7 @@ CPalThread::WaitForStartStatus( return m_fStartStatus; } -#if !HAVE_MACH_EXCEPTIONS +#if !HAVE_MACH_EXCEPTIONS && !defined(TARGET_SUNOS) /*++ Function : EnsureSignalAlternateStack