From 02f3ecdd195dd6ce04b3f3deb9562e1e21867307 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 12 Feb 2020 12:36:22 +0100 Subject: [PATCH] Display stack trace at stack overflow This is a fixed version of the reverted commit. This change enables printing stack trace at stack overflow to the console. In case multiple threads fail with stack overflow in parallel, only the trace of the first thread is printed. There are couple of interesting details: The stack trace is printed in a compressed form. The algorithm finds the largest sequence of frames starting from the top of the stack that is repeated and prints it just once with the repeat count. Only the first thread that hits stack overflow gets its stack printed. On Linux, others threads that hit stack overflow are held spinning until the process exits. This enables having a single preallocated stack for handling stack overflow. On Windows, we just don't print the stack trace, as it would be messy anyways due to the interleaving of output from multiple threads and the value of getting stack overflow traces of multiple threads is questionable. On debug / checked builds for Windows, I had to bump the stack guarantee by two pages and also enable setting the stack guarantee for all threads in these build flavors. At couple of places in the runtime, there were debug checks comparing explicit frame and some other struct addresses to the current SP. These were firing on Linux when we are running on an extra stack overflow handling stack. I've fixed it by adding a flag to the Thread that indicates that we are running on an alternate stack and these checks should be skipped. I've fixed the x86 Windows JIT_StackProbe to first probe at SP-4 and then move the SP to leave more stack space for the handler. I've fixed stack overflow check on Linux for some glibc / distros. The stack limit returned by the pthread_attr_getstack returns the address of the guard page in some of the glibc / distros and address of the last accessible page before the guard page on others. So I've relaxed the check to consider +/- 1 page around the stack limit to recognize sigsegv as stack overflow. --- src/coreclr/src/debug/ee/controller.cpp | 2 +- src/coreclr/src/inc/ex.h | 2 +- .../src/arch/amd64/signalhandlerhelper.cpp | 20 +- .../pal/src/arch/arm/signalhandlerhelper.cpp | 18 +- .../src/arch/arm64/signalhandlerhelper.cpp | 17 +- .../pal/src/arch/i386/signalhandlerhelper.cpp | 16 +- src/coreclr/src/pal/src/exception/seh.cpp | 15 +- src/coreclr/src/pal/src/exception/signal.cpp | 108 ++++++++--- .../src/pal/src/include/pal/signal.hpp | 9 +- src/coreclr/src/pal/src/thread/thread.cpp | 2 +- src/coreclr/src/utilcode/ex.cpp | 4 +- src/coreclr/src/vm/amd64/JitHelpers_Fast.asm | 2 +- src/coreclr/src/vm/amd64/jithelpers_fast.S | 4 +- src/coreclr/src/vm/arm/asmhelpers.S | 4 +- src/coreclr/src/vm/arm/asmhelpers.asm | 4 +- src/coreclr/src/vm/eedbginterface.h | 2 +- src/coreclr/src/vm/eedbginterfaceimpl.cpp | 6 +- src/coreclr/src/vm/eedbginterfaceimpl.h | 2 +- src/coreclr/src/vm/eepolicy.cpp | 178 ++++++++++++++---- src/coreclr/src/vm/excep.cpp | 30 ++- src/coreclr/src/vm/excep.h | 2 + src/coreclr/src/vm/exceptionhandling.cpp | 7 + src/coreclr/src/vm/frames.cpp | 3 +- src/coreclr/src/vm/i386/jithelp.S | 12 +- src/coreclr/src/vm/i386/jithelp.asm | 19 +- src/coreclr/src/vm/stackwalk.cpp | 4 +- src/coreclr/src/vm/threads.cpp | 12 +- src/coreclr/src/vm/threads.h | 19 +- 28 files changed, 390 insertions(+), 133 deletions(-) diff --git a/src/coreclr/src/debug/ee/controller.cpp b/src/coreclr/src/debug/ee/controller.cpp index 03b27612e5a5e6..137fcf774067db 100644 --- a/src/coreclr/src/debug/ee/controller.cpp +++ b/src/coreclr/src/debug/ee/controller.cpp @@ -8961,7 +8961,7 @@ bool DebuggerContinuableExceptionBreakpoint::SendEvent(Thread *thread, bool fIpC CONTEXT contextToAdjust; BOOL adjustedContext = FALSE; memcpy(&contextToAdjust, pContext, sizeof(CONTEXT)); - adjustedContext = g_pEEInterface->AdjustContextForWriteBarrierForDebugger(&contextToAdjust); + adjustedContext = g_pEEInterface->AdjustContextForJITHelpersForDebugger(&contextToAdjust); if (adjustedContext) { LOG((LF_CORDB, LL_INFO10000, "D::DDBP: HIT DATA BREAKPOINT INSIDE WRITE BARRIER...\n")); diff --git a/src/coreclr/src/inc/ex.h b/src/coreclr/src/inc/ex.h index 4037e51cd0ad37..21ce9d3d4b0ec7 100644 --- a/src/coreclr/src/inc/ex.h +++ b/src/coreclr/src/inc/ex.h @@ -112,7 +112,7 @@ void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result); // We save current ExceptionPointers using VectoredExceptionHandler. The save data is only valid // duing exception handling. GetCurrentExceptionPointers returns the saved data. // --------------------------------------------------------------------------- -void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo); +void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo DEBUG_ARG(bool checkExceptionRecordLocation)); // --------------------------------------------------------------------------- // We save current ExceptionPointers using VectoredExceptionHandler. The save data is only valid diff --git a/src/coreclr/src/pal/src/arch/amd64/signalhandlerhelper.cpp b/src/coreclr/src/pal/src/arch/amd64/signalhandlerhelper.cpp index 28232251b13187..61eba90008d959 100644 --- a/src/coreclr/src/pal/src/arch/amd64/signalhandlerhelper.cpp +++ b/src/coreclr/src/pal/src/arch/amd64/signalhandlerhelper.cpp @@ -13,10 +13,11 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do /*++ Function : - signal_handler_worker + ExecuteHandlerOnCustomStack - Handles signal on the original stack where the signal occured. - Invoked via setcontext. + Execute signal handler on a custom stack, the current stack pointer is specified by the customSp + If the customSp is 0, then the handler is executed on the original stack where the signal was fired. + It installs a fake stack frame to enable stack unwinding to the signal source location. Parameters : POSIX signal handler parameter list ("man sigaction" for details) @@ -24,13 +25,19 @@ Parameters : (no return value) --*/ -void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) +void ExecuteHandlerOnCustomStack(int code, siginfo_t *siginfo, void *context, size_t customSp, SignalHandlerWorkerReturnPoint* returnPoint) { ucontext_t *ucontext = (ucontext_t *)context; size_t faultSp = (size_t)MCREG_Rsp(ucontext->uc_mcontext); _ASSERTE(IS_ALIGNED(faultSp, 8)); + if (customSp == 0) + { + // preserve 128 bytes long red zone and align stack pointer + customSp = ALIGN_DOWN(faultSp - 128, 16); + } + size_t fakeFrameReturnAddress; if (IS_ALIGNED(faultSp, 16)) @@ -42,8 +49,7 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset8 + (size_t)CallSignalHandlerWrapper8; } - // preserve 128 bytes long red zone and align stack pointer - size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 128, 16); + size_t* sp = (size_t*)customSp; // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction *--sp = (size_t)MCREG_Rip(ucontext->uc_mcontext); @@ -51,7 +57,7 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, size_t fp = (size_t)sp; *--sp = fakeFrameReturnAddress; - // Switch the current context to the signal_handler_worker and the original stack + // Switch the current context to the signal_handler_worker and the custom stack CONTEXT context2; RtlCaptureContext(&context2); diff --git a/src/coreclr/src/pal/src/arch/arm/signalhandlerhelper.cpp b/src/coreclr/src/pal/src/arch/arm/signalhandlerhelper.cpp index eac3b29b81df8b..9c6dda1f2fb7b5 100644 --- a/src/coreclr/src/pal/src/arch/arm/signalhandlerhelper.cpp +++ b/src/coreclr/src/pal/src/arch/arm/signalhandlerhelper.cpp @@ -13,10 +13,11 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do /*++ Function : - signal_handler_worker + ExecuteHandlerOnCustomStack - Handles signal on the original stack where the signal occured. - Invoked via setcontext. + Execute signal handler on a custom stack, the current stack pointer is specified by the customSp + If the customSp is 0, then the handler is executed on the original stack where the signal was fired. + It installs a fake stack frame to enable stack unwinding to the signal source location. Parameters : POSIX signal handler parameter list ("man sigaction" for details) @@ -24,13 +25,19 @@ Parameters : (no return value) --*/ -void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) +void ExecuteHandlerOnCustomStack(int code, siginfo_t *siginfo, void *context, size_t customSp, SignalHandlerWorkerReturnPoint* returnPoint) { ucontext_t *ucontext = (ucontext_t *)context; size_t faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext); _ASSERTE(IS_ALIGNED(faultSp, 4)); + if (customSp == 0) + { + // preserve 8 bytes long red zone and align stack pointer + customSp = ALIGN_DOWN(faultSp - 8, 8); + } + size_t fakeFrameReturnAddress; if (IS_ALIGNED(faultSp, 8)) @@ -42,8 +49,7 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, fakeFrameReturnAddress = (size_t)SignalHandlerWorkerReturnOffset4 + (size_t)CallSignalHandlerWrapper4; } - // preserve 8 bytes long red zone and align stack pointer - size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 8, 8); + size_t* sp = (size_t*)customSp; #ifndef __linux__ size_t cpsr = (size_t)MCREG_Cpsr(ucontext->uc_mcontext); diff --git a/src/coreclr/src/pal/src/arch/arm64/signalhandlerhelper.cpp b/src/coreclr/src/pal/src/arch/arm64/signalhandlerhelper.cpp index b52e8a64d19af1..524bd11b364f8d 100644 --- a/src/coreclr/src/pal/src/arch/arm64/signalhandlerhelper.cpp +++ b/src/coreclr/src/pal/src/arch/arm64/signalhandlerhelper.cpp @@ -13,10 +13,11 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do /*++ Function : - signal_handler_worker + ExecuteHandlerOnCustomStack - Handles signal on the original stack where the signal occured. - Invoked via setcontext. + Execute signal handler on a custom stack, the current stack pointer is specified by the customSp + If the customSp is 0, then the handler is executed on the original stack where the signal was fired. + It installs a fake stack frame to enable stack unwinding to the signal source location. Parameters : POSIX signal handler parameter list ("man sigaction" for details) @@ -24,12 +25,18 @@ Parameters : (no return value) --*/ -void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) +void ExecuteHandlerOnCustomStack(int code, siginfo_t *siginfo, void *context, size_t customSp, SignalHandlerWorkerReturnPoint* returnPoint) { ucontext_t *ucontext = (ucontext_t *)context; size_t faultSp = (size_t)MCREG_Sp(ucontext->uc_mcontext); _ASSERTE(IS_ALIGNED(faultSp, 8)); + if (customSp == 0) + { + // preserve 128 bytes long red zone and align stack pointer + customSp = ALIGN_DOWN(faultSp - 128, 16); + } + size_t fakeFrameReturnAddress; if (IS_ALIGNED(faultSp, 16)) @@ -42,7 +49,7 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, } // preserve 128 bytes long red zone and align stack pointer - size_t* sp = (size_t*)ALIGN_DOWN(faultSp - 128, 16); + size_t* sp = (size_t*)customSp; // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction // pushed LR diff --git a/src/coreclr/src/pal/src/arch/i386/signalhandlerhelper.cpp b/src/coreclr/src/pal/src/arch/i386/signalhandlerhelper.cpp index 8e23099193028b..16e527a1ebcb83 100644 --- a/src/coreclr/src/pal/src/arch/i386/signalhandlerhelper.cpp +++ b/src/coreclr/src/pal/src/arch/i386/signalhandlerhelper.cpp @@ -13,10 +13,11 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do /*++ Function : - signal_handler_worker + ExecuteHandlerOnCustomStack - Handles signal on the original stack where the signal occured. - Invoked via setcontext. + Execute signal handler on a custom stack, the current stack pointer is specified by the customSp + If the customSp is 0, then the handler is executed on the original stack where the signal was fired. + It installs a fake stack frame to enable stack unwinding to the signal source location. Parameters : POSIX signal handler parameter list ("man sigaction" for details) @@ -24,13 +25,18 @@ Parameters : (no return value) --*/ -void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint) +void ExecuteHandlerOnCustomStack(int code, siginfo_t *siginfo, void *context, size_t customSp, SignalHandlerWorkerReturnPoint* returnPoint) { ucontext_t *ucontext = (ucontext_t *)context; size_t faultSp = (size_t)MCREG_Esp(ucontext->uc_mcontext); _ASSERTE(IS_ALIGNED(faultSp, 4)); + if (customSp == 0) + { + customSp = ALIGN_DOWN(faultSp, 16); + } + size_t fakeFrameReturnAddress; switch (faultSp & 0xc) @@ -49,7 +55,7 @@ void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, break; } - size_t* sp = (size_t*)ALIGN_DOWN(faultSp, 16); + size_t* sp = (size_t*)customSp; // Build fake stack frame to enable the stack unwinder to unwind from signal_handler_worker to the faulting instruction *--sp = (size_t)MCREG_Eip(ucontext->uc_mcontext); diff --git a/src/coreclr/src/pal/src/exception/seh.cpp b/src/coreclr/src/pal/src/exception/seh.cpp index ef95f5e354a3d1..e0ebff145b893d 100644 --- a/src/coreclr/src/pal/src/exception/seh.cpp +++ b/src/coreclr/src/pal/src/exception/seh.cpp @@ -28,6 +28,7 @@ Module Name: #include "pal/process.h" #include "pal/malloc.hpp" #include "pal/signal.hpp" +#include "pal/virtual.h" #if HAVE_MACH_EXCEPTIONS #include "machexception.h" @@ -268,14 +269,16 @@ SEHProcessException(PAL_SEHException* exception) // Check if the failed access has hit a stack guard page. In such case, it // was a stack probe that detected that there is not enough stack left. void* stackLimit = CPalThread::GetStackLimit(); - void* stackGuard = (void*)((size_t)stackLimit - getpagesize()); + void* stackOverflowBottom = (void*)((size_t)stackLimit - GetVirtualPageSize()); + // On some versions of glibc / platforms the stackLimit is an address of the guard page, on some + // it is right above the guard page. + // So consider SIGSEGV in one page above and below stack limit to be stack overflow. + void* stackOverflowTop = (void*)((size_t)stackLimit + GetVirtualPageSize()); void* violationAddr = (void*)exceptionRecord->ExceptionInformation[1]; - if ((violationAddr >= stackGuard) && (violationAddr < stackLimit)) + + if ((violationAddr >= stackOverflowBottom) && (violationAddr < stackOverflowTop)) { - // The exception happened in the page right below the stack limit, - // so it is a stack overflow - (void)write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1); - PROCAbort(); + exceptionRecord->ExceptionCode = EXCEPTION_STACK_OVERFLOW; } } diff --git a/src/coreclr/src/pal/src/exception/signal.cpp b/src/coreclr/src/pal/src/exception/signal.cpp index 49fdec8db89e40..d6d8256610e5c2 100644 --- a/src/coreclr/src/pal/src/exception/signal.cpp +++ b/src/coreclr/src/pal/src/exception/signal.cpp @@ -113,6 +113,9 @@ struct sigaction g_previous_activation; // Offset of the local variable containing pointer to windows style context in the common_signal_handler function. // This offset is relative to the frame pointer. int g_common_signal_handler_context_locvar_offset = 0; + +// TOP of special stack for handling stack overflow +volatile void* g_stackOverflowHandlerStack = NULL; #endif // !HAVE_MACH_EXCEPTIONS /* public function definitions ************************************************/ @@ -175,6 +178,26 @@ BOOL SEHInitializeSignals(CorUnix::CPalThread *pthrCurrent, DWORD flags) { return FALSE; } + + // Allocate the minimal stack necessary for handling stack overflow + int stackOverflowStackSize = ALIGN_UP(sizeof(SignalHandlerWorkerReturnPoint), 16) + 7 * 4096; + // Align the size to virtual page size and add one virtual page as a stack guard + stackOverflowStackSize = ALIGN_UP(stackOverflowStackSize, GetVirtualPageSize()) + GetVirtualPageSize(); + g_stackOverflowHandlerStack = mmap(NULL, stackOverflowStackSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_STACK | MAP_PRIVATE, -1, 0); + if (g_stackOverflowHandlerStack == MAP_FAILED) + { + return FALSE; + } + + // create a guard page for the alternate stack + int st = mprotect((void*)g_stackOverflowHandlerStack, GetVirtualPageSize(), PROT_NONE); + if (st != 0) + { + munmap((void*)g_stackOverflowHandlerStack, stackOverflowStackSize); + return FALSE; + } + + g_stackOverflowHandlerStack = (void*)((size_t)g_stackOverflowHandlerStack + stackOverflowStackSize); } /* The default action for SIGPIPE is process termination. @@ -430,6 +453,41 @@ bool IsRunningOnAlternateStack(void *context) return isRunningOnAlternateStack; } +/*++ +Function : + SwitchStackAndExecuteHandler + + Switch to the stack specified by the sp argument + +Parameters : + POSIX signal handler parameter list ("man sigaction" for details) + sp - stack pointer of the stack to execute the handler on. + If sp == 0, execute it on the original stack where the signal has occured. +Return : + The return value from the signal handler +--*/ +static bool SwitchStackAndExecuteHandler(int code, siginfo_t *siginfo, void *context, size_t sp) +{ + // Establish a return point in case the common_signal_handler returns + + volatile bool contextInitialization = true; + + void *ptr = alloca(sizeof(SignalHandlerWorkerReturnPoint) + alignof(SignalHandlerWorkerReturnPoint) - 1); + SignalHandlerWorkerReturnPoint *pReturnPoint = (SignalHandlerWorkerReturnPoint *)ALIGN_UP(ptr, alignof(SignalHandlerWorkerReturnPoint)); + RtlCaptureContext(&pReturnPoint->context); + + // When the signal handler worker completes, it uses setcontext to return to this point + + if (contextInitialization) + { + contextInitialization = false; + ExecuteHandlerOnCustomStack(code, siginfo, context, sp, pReturnPoint); + _ASSERTE(FALSE); // The ExecuteHandlerOnCustomStack should never return + } + + return pReturnPoint->returnFromHandler; +} + /*++ Function : sigsegv_handler @@ -453,8 +511,30 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) // we have a stack overflow. if ((failureAddress - (sp - GetVirtualPageSize())) < 2 * GetVirtualPageSize()) { - (void)write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1); - PROCAbort(); + if (GetCurrentPalThread()) + { + size_t handlerStackTop = __sync_val_compare_and_swap((size_t*)&g_stackOverflowHandlerStack, (size_t)g_stackOverflowHandlerStack, 0); + if (handlerStackTop == 0) + { + // We have only one stack for handling stack overflow preallocated. We let only the first thread that hits stack overflow to + // run the exception handling code on that stack (which ends up just dumping the stack trace and aborting the process). + // Other threads are held spinning and sleeping here until the process exits. + while (true) + { + sleep(1); + } + } + + if (SwitchStackAndExecuteHandler(code, siginfo, context, (size_t)handlerStackTop)) + { + PROCAbort(); + } + } + else + { + (void)write(STDERR_FILENO, StackOverflowMessage, sizeof(StackOverflowMessage) - 1); + PROCAbort(); + } } // Now that we know the SIGSEGV didn't happen due to a stack overflow, execute the common @@ -462,24 +542,7 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) if (GetCurrentPalThread() && IsRunningOnAlternateStack(context)) { - // Establish a return point in case the common_signal_handler returns - - volatile bool contextInitialization = true; - - void *ptr = alloca(sizeof(SignalHandlerWorkerReturnPoint) + alignof(SignalHandlerWorkerReturnPoint) - 1); - SignalHandlerWorkerReturnPoint *pReturnPoint = (SignalHandlerWorkerReturnPoint *)ALIGN_UP(ptr, alignof(SignalHandlerWorkerReturnPoint)); - RtlCaptureContext(&pReturnPoint->context); - - // When the signal handler worker completes, it uses setcontext to return to this point - - if (contextInitialization) - { - contextInitialization = false; - ExecuteHandlerOnOriginalStack(code, siginfo, context, pReturnPoint); - _ASSERTE(FALSE); // The ExecuteHandlerOnOriginalStack should never return - } - - if (pReturnPoint->returnFromHandler) + if (SwitchStackAndExecuteHandler(code, siginfo, context, 0 /* sp */)) // sp == 0 indicates execution on the original stack { return; } @@ -692,7 +755,10 @@ PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) { #ifdef INJECT_ACTIVATION_SIGNAL int status = pthread_kill(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL); - if (status != 0) + // We can get EAGAIN when printing stack overflow stack trace and when other threads hit + // stack overflow too. Those are held in the sigsegv_handler with blocked signals until + // the process exits. + if ((status != 0) && (status != EAGAIN)) { // Failure to send the signal is fatal. There are only two cases when sending // the signal can fail. First, if the signal ID is invalid and second, diff --git a/src/coreclr/src/pal/src/include/pal/signal.hpp b/src/coreclr/src/pal/src/include/pal/signal.hpp index 51b098809447a0..7b5e1d71407b1c 100644 --- a/src/coreclr/src/pal/src/include/pal/signal.hpp +++ b/src/coreclr/src/pal/src/include/pal/signal.hpp @@ -78,10 +78,11 @@ extern "C" void signal_handler_worker(int code, siginfo_t *siginfo, void *contex /*++ Function : - ExecuteHandlerOnOriginalStack + ExecuteHandlerOnCustomStack - Executes signal_handler_worker on the original stack where the signal occured. - It installs fake stack frame to enable stack unwinding to the signal source location. + Execute signal handler on a custom stack, the current stack pointer is specified by the customSp + If the customSp is 0, then the handler is executed on the original stack where the signal was fired. + It installs a fake stack frame to enable stack unwinding to the signal source location. Parameters : POSIX signal handler parameter list ("man sigaction" for details) @@ -89,7 +90,7 @@ Parameters : (no return value) --*/ -void ExecuteHandlerOnOriginalStack(int code, siginfo_t *siginfo, void *context, SignalHandlerWorkerReturnPoint* returnPoint); +void ExecuteHandlerOnCustomStack(int code, siginfo_t *siginfo, void *context, size_t sp, SignalHandlerWorkerReturnPoint* returnPoint); #endif // !HAVE_MACH_EXCEPTIONS diff --git a/src/coreclr/src/pal/src/thread/thread.cpp b/src/coreclr/src/pal/src/thread/thread.cpp index c8dcc43d06f561..1473601c512523 100644 --- a/src/coreclr/src/pal/src/thread/thread.cpp +++ b/src/coreclr/src/pal/src/thread/thread.cpp @@ -2808,7 +2808,7 @@ PAL_InjectActivation( palError = InjectActivationInternal(pTargetThread); } - if (palError == NO_ERROR) + if (palError != NO_ERROR) { pCurrentThread->SetLastError(palError); } diff --git a/src/coreclr/src/utilcode/ex.cpp b/src/coreclr/src/utilcode/ex.cpp index 479a1e0fd08ab9..c9703262732971 100644 --- a/src/coreclr/src/utilcode/ex.cpp +++ b/src/coreclr/src/utilcode/ex.cpp @@ -1216,7 +1216,7 @@ void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result) #if !defined(DACCESS_COMPILE) -void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo) +void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo DEBUG_ARG(bool checkExceptionRecordLocation)) { WRAPPER_NO_CONTRACT; @@ -1227,7 +1227,7 @@ void GetCurrentExceptionPointers(PEXCEPTION_POINTERS pExceptionInfo) pExceptionInfo->ExceptionRecord = pRecord; #ifdef _DEBUG - if (pRecord != NULL) + if (pRecord != NULL && checkExceptionRecordLocation) { _ASSERTE ((PVOID)(pRecord) > (PVOID)(&pRecord)); } diff --git a/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm b/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm index 171e5a2c769e90..54c0019a944c63 100644 --- a/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm +++ b/src/coreclr/src/vm/amd64/JitHelpers_Fast.asm @@ -651,6 +651,6 @@ ProbeLoop: ret -LEAF_END JIT_StackProbe, _TEXT +LEAF_END_MARKED JIT_StackProbe, _TEXT end diff --git a/src/coreclr/src/vm/amd64/jithelpers_fast.S b/src/coreclr/src/vm/amd64/jithelpers_fast.S index 57f7b9ed9adf7a..d5c9eaec923775 100644 --- a/src/coreclr/src/vm/amd64/jithelpers_fast.S +++ b/src/coreclr/src/vm/amd64/jithelpers_fast.S @@ -550,7 +550,7 @@ LEAF_END JIT_Stelem_Ref__ArrayStoreCheck_Helper, _TEXT #define PAGE_SIZE 0x1000 -NESTED_ENTRY JIT_StackProbe, _TEXT, NoHandler +LEAF_ENTRY JIT_StackProbe, _TEXT // On entry: // r11 - points to the lowest address on the stack frame being allocated (i.e. [InitialSp - FrameSize]) // rsp - points to some byte on the last probed page @@ -577,4 +577,4 @@ LOCAL_LABEL(ProbeLoop): RESET_FRAME_WITH_RBP ret -NESTED_END JIT_StackProbe, _TEXT +LEAF_END_MARKED JIT_StackProbe, _TEXT diff --git a/src/coreclr/src/vm/arm/asmhelpers.S b/src/coreclr/src/vm/arm/asmhelpers.S index 68e6f08a035f59..34fb7235ff2f50 100644 --- a/src/coreclr/src/vm/arm/asmhelpers.S +++ b/src/coreclr/src/vm/arm/asmhelpers.S @@ -1466,7 +1466,7 @@ DelayLoad_Helper\suffix: #define PAGE_SIZE 0x1000 #define PAGE_SIZE_LOG2 12 - NESTED_ENTRY JIT_StackProbe, _TEXT, NoHandler + LEAF_ENTRY JIT_StackProbe, _TEXT PROLOG_PUSH "{r7}" PROLOG_STACK_SAVE r7 @@ -1484,4 +1484,4 @@ ProbeLoop: EPILOG_STACK_RESTORE r7 EPILOG_POP "{r7}" EPILOG_BRANCH_REG lr - NESTED_END JIT_StackProbe, _TEXT + LEAF_END_MARKED JIT_StackProbe, _TEXT diff --git a/src/coreclr/src/vm/arm/asmhelpers.asm b/src/coreclr/src/vm/arm/asmhelpers.asm index 1df5420770a80f..89cb80f00d2604 100644 --- a/src/coreclr/src/vm/arm/asmhelpers.asm +++ b/src/coreclr/src/vm/arm/asmhelpers.asm @@ -2160,7 +2160,7 @@ $__RealName ; ; NOTE: this helper will probe at least one page below the one pointed to by sp. #define PAGE_SIZE_LOG2 12 - NESTED_ENTRY JIT_StackProbe + LEAF_ENTRY JIT_StackProbe PROLOG_PUSH {r7} PROLOG_STACK_SAVE r7 @@ -2178,7 +2178,7 @@ ProbeLoop EPILOG_STACK_RESTORE r7 EPILOG_POP {r7} EPILOG_BRANCH_REG lr - NESTED_END + LEAF_END_MARKED JIT_StackProbe ; Must be at very end of file END diff --git a/src/coreclr/src/vm/eedbginterface.h b/src/coreclr/src/vm/eedbginterface.h index f00c4509ec829b..2d54b5445d1780 100644 --- a/src/coreclr/src/vm/eedbginterface.h +++ b/src/coreclr/src/vm/eedbginterface.h @@ -375,7 +375,7 @@ class EEDebugInterface #endif #ifndef DACCESS_COMPILE - virtual BOOL AdjustContextForWriteBarrierForDebugger(CONTEXT* context) = 0; + virtual BOOL AdjustContextForJITHelpersForDebugger(CONTEXT* context) = 0; #endif }; diff --git a/src/coreclr/src/vm/eedbginterfaceimpl.cpp b/src/coreclr/src/vm/eedbginterfaceimpl.cpp index 2c5580a0d36823..dc5a85c8df2f64 100644 --- a/src/coreclr/src/vm/eedbginterfaceimpl.cpp +++ b/src/coreclr/src/vm/eedbginterfaceimpl.cpp @@ -1584,11 +1584,11 @@ void EEDbgInterfaceImpl::ObjectRefFlush(Thread *pThread) #ifndef DACCESS_COMPILE -BOOL AdjustContextForWriteBarrier(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContext); -BOOL EEDbgInterfaceImpl::AdjustContextForWriteBarrierForDebugger(CONTEXT* context) +BOOL AdjustContextForJITHelpers(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContext); +BOOL EEDbgInterfaceImpl::AdjustContextForJITHelpersForDebugger(CONTEXT* context) { WRAPPER_NO_CONTRACT; - return AdjustContextForWriteBarrier(nullptr, context); + return AdjustContextForJITHelpers(nullptr, context); } #endif diff --git a/src/coreclr/src/vm/eedbginterfaceimpl.h b/src/coreclr/src/vm/eedbginterfaceimpl.h index 0d77f0b8fb5001..036aeb9ff493a9 100644 --- a/src/coreclr/src/vm/eedbginterfaceimpl.h +++ b/src/coreclr/src/vm/eedbginterfaceimpl.h @@ -345,7 +345,7 @@ class EEDbgInterfaceImpl : public EEDebugInterface #endif #ifndef DACCESS_COMPILE - virtual BOOL AdjustContextForWriteBarrierForDebugger(CONTEXT* context); + virtual BOOL AdjustContextForJITHelpersForDebugger(CONTEXT* context); #endif }; diff --git a/src/coreclr/src/vm/eepolicy.cpp b/src/coreclr/src/vm/eepolicy.cpp index e1de20a54b25c3..075737e0f8a34f 100644 --- a/src/coreclr/src/vm/eepolicy.cpp +++ b/src/coreclr/src/vm/eepolicy.cpp @@ -700,7 +700,7 @@ void EEPolicy::HandleStackOverflow(StackOverflowDetector detector, void * pLimit } EXCEPTION_POINTERS exceptionInfo; - GetCurrentExceptionPointers(&exceptionInfo); + GetCurrentExceptionPointers(&exceptionInfo DEBUG_ARG(!pThread->IsExecutingOnAltStack())); _ASSERTE(exceptionInfo.ExceptionRecord); @@ -739,33 +739,119 @@ void EEPolicy::HandleExitProcess(ShutdownCompleteAction sca) HandleExitProcessHelper(action, 0, sca); } -StackWalkAction LogCallstackForLogCallback( - CrawlFrame *pCF, // - VOID* pData // Caller's private data -) + +//--------------------------------------------------------------------------------------- +// This class is responsible for displaying a stack trace. It uses a condensed way for +// stack overflow stack traces where there are possibly many repeated frames. +// It displays a count and a repeated sequence of frames at the top of the stack in +// such a case, instead of displaying possibly thousands of lines with the same +// method. +//--------------------------------------------------------------------------------------- +class CallStackLogger { - CONTRACTL + // MethodDescs of the stack frames, the TOS is at index 0 + CDynArray m_frames; + + // Index of a stack frame where a possible repetition of frames starts + int m_commonStartIndex = -1; + // Length of the largest found repeated sequence of frames + int m_largestCommonStartLength = 0; + // Number of repetitions of the largest repeated sequence of frames + int m_largestCommonStartRepeat = 0; + + StackWalkAction LogCallstackForLogCallbackWorker(CrawlFrame *pCF) { - THROWS; - GC_TRIGGERS; - MODE_ANY; + WRAPPER_NO_CONTRACT; + + MethodDesc *pMD = pCF->GetFunction(); + + if (m_commonStartIndex != -1) + { + // Some common frames were already found + + if (m_frames[m_frames.Count() - m_commonStartIndex] != pMD) + { + // The frame being added is not part of the repeated sequence + if (m_frames.Count() / m_commonStartIndex >= 2) + { + // A sequence repeated at least twice was found. It is the largest one that was found so far + m_largestCommonStartLength = m_commonStartIndex; + m_largestCommonStartRepeat = m_frames.Count() / m_commonStartIndex; + } + + m_commonStartIndex = -1; + } + } + + if (m_commonStartIndex == -1) + { + if ((m_frames.Count() != 0) && (pMD == m_frames[0])) + { + // We have found a frame with the same MethodDesc as the frame at the top of the stack, + // possibly a new repeated sequence is starting. + m_commonStartIndex = m_frames.Count(); + } + } + + MethodDesc** itemPtr = m_frames.Append(); + if (itemPtr == nullptr) + { + // Memory allocation failure + return SWA_ABORT; + } + + *itemPtr = pMD; + + return SWA_CONTINUE; } - CONTRACTL_END; - SmallStackSString *pWordAt = ((SmallStackSString*)pData); + void PrintFrame(int index, const WCHAR* pWordAt) + { + WRAPPER_NO_CONTRACT; + + SString str(pWordAt); - MethodDesc *pMD = pCF->GetFunction(); - _ASSERTE(pMD != NULL); + MethodDesc* pMD = m_frames[index]; + TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); + PrintToStdErrW(str.GetUnicode()); + PrintToStdErrA("\n"); + } - StackSString str; - str = *pWordAt; +public: - TypeString::AppendMethodInternal(str, pMD, TypeString::FormatNamespace|TypeString::FormatFullInst|TypeString::FormatSignature); - PrintToStdErrW(str.GetUnicode()); - PrintToStdErrA("\n"); + // Callback called by the stack walker for each frame on the stack + static StackWalkAction LogCallstackForLogCallback(CrawlFrame *pCF, VOID* pData) + { + WRAPPER_NO_CONTRACT; - return SWA_CONTINUE; -} + CallStackLogger* logger = (CallStackLogger*)pData; + return logger->LogCallstackForLogCallbackWorker(pCF); + } + + void PrintStackTrace(const WCHAR* pWordAt) + { + WRAPPER_NO_CONTRACT; + + if (m_largestCommonStartLength != 0) + { + SmallStackSString repeatStr; + repeatStr.AppendPrintf("Repeat %d times:\n", m_largestCommonStartRepeat); + + PrintToStdErrW(repeatStr.GetUnicode()); + PrintToStdErrA("--------------------------------\n"); + for (int i = 0; i < m_largestCommonStartLength; i++) + { + PrintFrame(i, pWordAt); + } + PrintToStdErrA("--------------------------------\n"); + } + + for (int i = m_largestCommonStartLength * m_largestCommonStartRepeat; i < m_frames.Count(); i++) + { + PrintFrame(i, pWordAt); + } + } +}; //--------------------------------------------------------------------------------------- // @@ -777,14 +863,16 @@ StackWalkAction LogCallstackForLogCallback( // Return Value: // None // -inline void LogCallstackForLogWorker() +inline void LogCallstackForLogWorker(bool isStackOverflow = false) { + WRAPPER_NO_CONTRACT; + Thread* pThread = GetThread(); _ASSERTE (pThread); SmallStackSString WordAt; - if (!WordAt.LoadResource(CCompRC::Optional, IDS_ER_WORDAT)) + if (isStackOverflow || !WordAt.LoadResource(CCompRC::Optional, IDS_ER_WORDAT)) { WordAt.Set(W(" at")); } @@ -794,7 +882,12 @@ inline void LogCallstackForLogWorker() } WordAt += W(" "); - pThread->StackWalkFrames(&LogCallstackForLogCallback, &WordAt, QUICKUNWIND | FUNCTIONSONLY); + CallStackLogger logger; + + pThread->StackWalkFrames(&CallStackLogger::LogCallstackForLogCallback, &logger, QUICKUNWIND | FUNCTIONSONLY); + + logger.PrintStackTrace(WordAt.GetUnicode()); + } //--------------------------------------------------------------------------------------- @@ -1056,7 +1149,35 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleFatalStackOverflow\n"); - DisplayStackOverflowException(); + FrameWithCookie fef; +#if defined(FEATURE_EH_FUNCLETS) + *((&fef)->GetGSCookiePtr()) = GetProcessGSCookie(); +#endif // FEATURE_EH_FUNCLETS + if (pExceptionInfo && pExceptionInfo->ContextRecord) + { + GCX_COOP(); + AdjustContextForJITHelpers(pExceptionInfo->ExceptionRecord, pExceptionInfo->ContextRecord); + fef.InitAndLink(pExceptionInfo->ContextRecord); + } + + static volatile LONG g_stackOverflowCallStackLogged = 0; + + // Dump stack trace only for the first thread failing with stack overflow to prevent mixing + // multiple stack traces together. + if (InterlockedCompareExchange(&g_stackOverflowCallStackLogged, 1, 0) == 0) + { + DisplayStackOverflowException(); + LogCallstackForLogWorker(true /* isStackOverflow */); + g_stackOverflowCallStackLogged = 2; + } + else + { + // Wait for the thread that is logging the stack trace to complete + while (g_stackOverflowCallStackLogged != 2) + { + Sleep(50); + } + } if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_DOTNET_Context, FailFast)) { @@ -1097,15 +1218,6 @@ void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pE fTreatAsNativeUnhandledException = TRUE; } } - FrameWithCookie fef; -#if defined(FEATURE_EH_FUNCLETS) - *((&fef)->GetGSCookiePtr()) = GetProcessGSCookie(); -#endif // FEATURE_EH_FUNCLETS - if (pExceptionInfo && pExceptionInfo->ContextRecord) - { - GCX_COOP(); - fef.InitAndLink(pExceptionInfo->ContextRecord); - } #ifndef TARGET_UNIX if (IsWatsonEnabled() && (g_pDebugInterface != NULL)) diff --git a/src/coreclr/src/vm/excep.cpp b/src/coreclr/src/vm/excep.cpp index 3394a3fc01c4f2..35a39f359cecc8 100644 --- a/src/coreclr/src/vm/excep.cpp +++ b/src/coreclr/src/vm/excep.cpp @@ -6638,6 +6638,10 @@ IsDebuggerFault(EXCEPTION_RECORD *pExceptionRecord, #endif // TARGET_UNIX +#ifndef TARGET_ARM64 +EXTERN_C void JIT_StackProbe_End(); +#endif // TARGET_ARM64 + #ifdef FEATURE_EH_FUNCLETS #ifndef TARGET_X86 @@ -6703,6 +6707,9 @@ bool IsIPInMarkedJitHelper(UINT_PTR uControlPc) CHECK_RANGE(JIT_WriteBarrier) CHECK_RANGE(JIT_CheckedWriteBarrier) CHECK_RANGE(JIT_ByRefWriteBarrier) +#if !defined(TARGET_ARM64) + CHECK_RANGE(JIT_StackProbe) +#endif // !TARGET_ARM64 #else #ifdef TARGET_UNIX CHECK_RANGE(JIT_WriteBarrierGroup) @@ -6720,7 +6727,7 @@ bool IsIPInMarkedJitHelper(UINT_PTR uControlPc) // Returns TRUE if caller should resume execution. BOOL -AdjustContextForWriteBarrier( +AdjustContextForJITHelpers( EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContext) { @@ -6739,7 +6746,7 @@ AdjustContextForWriteBarrier( #ifdef FEATURE_DATABREAKPOINT - // If pExceptionRecord is null, it means it is called from EEDbgInterfaceImpl::AdjustContextForWriteBarrierForDebugger() + // If pExceptionRecord is null, it means it is called from EEDbgInterfaceImpl::AdjustContextForJITHelpersForDebugger() // This is called only when a data breakpoint is hitm which could be inside a JIT write barrier helper and required // this logic to help unwind out of it. For the x86, not patched case, we assume the IP lies within the region where we // have already saved the registers on the stack, and therefore the code unwind those registers as well. This is not true @@ -6792,6 +6799,19 @@ AdjustContextForWriteBarrier( // put ESP back to what it was before the call. SetSP(pContext, PCODE((BYTE*)GetSP(pContext) + sizeof(void*))); } + + if ((f_IP >= (void *) JIT_StackProbe) && (f_IP <= (void *) JIT_StackProbe_End)) + { + TADDR ebp = GetFP(pContext); + void* callsite = (void *)*dac_cast(ebp + 4); + pExceptionRecord->ExceptionAddress = callsite; + SetIP(pContext, (PCODE)callsite); + + // Restore EBP / ESP back to what it was before the call. + SetFP(pContext, *dac_cast(ebp)); + SetSP(pContext, ebp + 8); + } + return FALSE; #elif defined(FEATURE_EH_FUNCLETS) // TARGET_X86 && !TARGET_UNIX void* f_IP = dac_cast(GetIP(pContext)); @@ -6860,7 +6880,7 @@ AdjustContextForWriteBarrier( return FALSE; #else // FEATURE_EH_FUNCLETS - PORTABILITY_ASSERT("AdjustContextForWriteBarrier"); + PORTABILITY_ASSERT("AdjustContextForJITHelpers"); return FALSE; #endif // ELSE } @@ -7467,9 +7487,9 @@ VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExcepti { if (IsWellFormedAV(pExceptionRecord)) { - if (AdjustContextForWriteBarrier(pExceptionRecord, pContext)) + if (AdjustContextForJITHelpers(pExceptionRecord, pContext)) { - // On x86, AdjustContextForWriteBarrier simply backs up AV's + // On x86, AdjustContextForJITHelpers simply backs up AV's // in write barrier helpers into the calling frame, so that // the subsequent logic here sees a managed fault. // diff --git a/src/coreclr/src/vm/excep.h b/src/coreclr/src/vm/excep.h index 7f9aba1b30995a..b6fb51242dc1f2 100644 --- a/src/coreclr/src/vm/excep.h +++ b/src/coreclr/src/vm/excep.h @@ -32,6 +32,8 @@ BOOL IsIPinVirtualStub(PCODE f_IP); #endif // VSD_STUB_CAN_THROW_AV bool IsIPInMarkedJitHelper(UINT_PTR uControlPc); +BOOL AdjustContextForJITHelpers(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContext); + #if defined(FEATURE_HIJACK) && (!defined(TARGET_X86) || defined(TARGET_UNIX)) // General purpose functions for use on an IP in jitted code. diff --git a/src/coreclr/src/vm/exceptionhandling.cpp b/src/coreclr/src/vm/exceptionhandling.cpp index 827637b53a30aa..e473814af65c12 100644 --- a/src/coreclr/src/vm/exceptionhandling.cpp +++ b/src/coreclr/src/vm/exceptionhandling.cpp @@ -5217,6 +5217,13 @@ BOOL HandleHardwareException(PAL_SEHException* ex) { _ASSERTE(IsSafeToHandleHardwareException(ex->GetContextRecord(), ex->GetExceptionRecord())); + if (ex->GetExceptionRecord()->ExceptionCode == EXCEPTION_STACK_OVERFLOW) + { + GetThread()->SetExecutingOnAltStack(); + EEPolicy::HandleFatalStackOverflow(&ex->ExceptionPointers, FALSE); + UNREACHABLE(); + } + if (ex->GetExceptionRecord()->ExceptionCode != STATUS_BREAKPOINT && ex->GetExceptionRecord()->ExceptionCode != STATUS_SINGLE_STEP) { // A hardware exception is handled only if it happened in a jitted code or diff --git a/src/coreclr/src/vm/frames.cpp b/src/coreclr/src/vm/frames.cpp index fafc7d3d681172..9210323fcc5ea1 100644 --- a/src/coreclr/src/vm/frames.cpp +++ b/src/coreclr/src/vm/frames.cpp @@ -402,7 +402,8 @@ VOID Frame::Push(Thread *pThread) // in which the C compiler will lay them out in the stack frame. // So GetOsPageSize() is a guess of the maximum stack frame size of any method // with multiple Frames in mscorwks.dll - _ASSERTE(((m_Next == FRAME_TOP) || + _ASSERTE(pThread->IsExecutingOnAltStack() || + ((m_Next == FRAME_TOP) || (PBYTE(m_Next) + (2 * GetOsPageSize())) > PBYTE(this)) && "Pushing a frame out of order ?"); diff --git a/src/coreclr/src/vm/i386/jithelp.S b/src/coreclr/src/vm/i386/jithelp.S index 49a5aa16307ed6..86a25f98d50638 100644 --- a/src/coreclr/src/vm/i386/jithelp.S +++ b/src/coreclr/src/vm/i386/jithelp.S @@ -27,8 +27,8 @@ // // ******************************************************************************* -// The code here is tightly coupled with AdjustContextForWriteBarrier, if you change -// anything here, you might need to change AdjustContextForWriteBarrier as well +// The code here is tightly coupled with AdjustContextForJITHelpers, if you change +// anything here, you might need to change AdjustContextForJITHelpers as well .macro WriteBarrierHelper rg .align 4 @@ -80,7 +80,7 @@ PATCH_LABEL JIT_DebugWriteBarrier\rg #ifdef WRITE_BARRIER_CHECK // Test dest here so if it is bad AV would happen before we change register/stack - // status. This makes job of AdjustContextForWriteBarrier easier. + // status. This makes job of AdjustContextForJITHelpers easier. cmp BYTE PTR [edx], 0 // ALSO update the shadow GC heap if that is enabled // Make ebp into the temporary src register. We need to do this so that we can use ecx @@ -226,8 +226,8 @@ NESTED_END JIT_CheckedWriteBarrier\rg, _TEXT // // ******************************************************************************* // -// The code here is tightly coupled with AdjustContextForWriteBarrier, if you change -// anything here, you might need to change AdjustContextForWriteBarrier as well +// The code here is tightly coupled with AdjustContextForJITHelpers, if you change +// anything here, you might need to change AdjustContextForJITHelpers as well // .macro ByRefWriteBarrierHelper .align 4 @@ -253,7 +253,7 @@ LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT #ifdef WRITE_BARRIER_CHECK // Test dest here so if it is bad AV would happen before we change register/stack - // status. This makes job of AdjustContextForWriteBarrier easier. + // status. This makes job of AdjustContextForJITHelpers easier. cmp BYTE PTR [edi], 0 // ALSO update the shadow GC heap if that is enabled diff --git a/src/coreclr/src/vm/i386/jithelp.asm b/src/coreclr/src/vm/i386/jithelp.asm index 14c39755238304..3eec4408cfab43 100644 --- a/src/coreclr/src/vm/i386/jithelp.asm +++ b/src/coreclr/src/vm/i386/jithelp.asm @@ -124,8 +124,8 @@ ENDM ; ;******************************************************************************* -; The code here is tightly coupled with AdjustContextForWriteBarrier, if you change -; anything here, you might need to change AdjustContextForWriteBarrier as well +; The code here is tightly coupled with AdjustContextForJITHelpers, if you change +; anything here, you might need to change AdjustContextForJITHelpers as well ; Note that beside the AV case, we might be unwinding inside the region where we have ; already push ecx and ebp in the branch under FEATURE_DATABREAKPOINT WriteBarrierHelper MACRO rg @@ -176,7 +176,7 @@ endif ifdef WRITE_BARRIER_CHECK ; Test dest here so if it is bad AV would happen before we change register/stack - ; status. This makes job of AdjustContextForWriteBarrier easier. + ; status. This makes job of AdjustContextForJITHelpers easier. cmp [edx], 0 ;; ALSO update the shadow GC heap if that is enabled ; Make ebp into the temporary src register. We need to do this so that we can use ecx @@ -293,8 +293,8 @@ ENDM ; ;******************************************************************************* -; The code here is tightly coupled with AdjustContextForWriteBarrier, if you change -; anything here, you might need to change AdjustContextForWriteBarrier as well +; The code here is tightly coupled with AdjustContextForJITHelpers, if you change +; anything here, you might need to change AdjustContextForJITHelpers as well ByRefWriteBarrierHelper MACRO ALIGN 4 @@ -314,7 +314,7 @@ endif ifdef WRITE_BARRIER_CHECK ; Test dest here so if it is bad AV would happen before we change register/stack - ; status. This makes job of AdjustContextForWriteBarrier easier. + ; status. This makes job of AdjustContextForJITHelpers easier. cmp [edi], 0 ;; ALSO update the shadow GC heap if that is enabled @@ -1337,8 +1337,8 @@ _JIT_StackProbe@0 PROC public and esp, -PAGE_SIZE ; esp points to the **lowest address** on the last probed page ; This is done to make the loop end condition simpler. ProbeLoop: + test [esp - 4], eax ; esp points to the lowest address on the **last probed** page sub esp, PAGE_SIZE ; esp points to the lowest address of the **next page** to probe - test [esp], eax ; esp points to the lowest address on the **last probed** page cmp esp, eax jg ProbeLoop ; if esp > eax, then we need to probe at least one more page. @@ -1348,4 +1348,9 @@ ProbeLoop: _JIT_StackProbe@0 ENDP +PUBLIC _JIT_StackProbe_End@0 +_JIT_StackProbe_End@0 PROC + ret +_JIT_StackProbe_End@0 ENDP + end diff --git a/src/coreclr/src/vm/stackwalk.cpp b/src/coreclr/src/vm/stackwalk.cpp index 0251b05724f8c8..66890ec1002a6c 100644 --- a/src/coreclr/src/vm/stackwalk.cpp +++ b/src/coreclr/src/vm/stackwalk.cpp @@ -2570,9 +2570,9 @@ StackWalkAction StackFrameIterator::NextRaw(void) PTR_VOID newSP = PTR_VOID((TADDR)GetRegdisplaySP(m_crawl.pRD)); #ifndef NO_FIXED_STACK_LIMIT - FAIL_IF_SPECULATIVE_WALK(newSP >= m_crawl.pThread->GetCachedStackLimit()); + FAIL_IF_SPECULATIVE_WALK(m_crawl.pThread->IsExecutingOnAltStack() || newSP >= m_crawl.pThread->GetCachedStackLimit()); #endif // !NO_FIXED_STACK_LIMIT - FAIL_IF_SPECULATIVE_WALK(newSP < m_crawl.pThread->GetCachedStackBase()); + FAIL_IF_SPECULATIVE_WALK(m_crawl.pThread->IsExecutingOnAltStack() || newSP < m_crawl.pThread->GetCachedStackBase()); #undef FAIL_IF_SPECULATIVE_WALK diff --git a/src/coreclr/src/vm/threads.cpp b/src/coreclr/src/vm/threads.cpp index da222bc10253f4..f9586156b7b555 100644 --- a/src/coreclr/src/vm/threads.cpp +++ b/src/coreclr/src/vm/threads.cpp @@ -184,8 +184,8 @@ void Thread::SetFrame(Frame *pFrame) if (pFrame == stopFrame) _ASSERTE(!"SetFrame frame == stopFrame"); - _ASSERTE(espVal < pFrame); - _ASSERTE(pFrame < m_CacheStackBase); + _ASSERTE(IsExecutingOnAltStack() || espVal < pFrame); + _ASSERTE(IsExecutingOnAltStack() || pFrame < m_CacheStackBase); _ASSERTE(pFrame->GetFrameType() < Frame::TYPE_COUNT); pFrame = pFrame->m_Next; @@ -6481,7 +6481,7 @@ HRESULT Thread::CLRSetThreadStackGuarantee(SetThreadStackGuaranteeScope fScope) // -additionally, we need to provide some region to hosts to allow for lock acquisition in a hosted scenario // EXTRA_PAGES = 3; - INDEBUG(EXTRA_PAGES += 1); + INDEBUG(EXTRA_PAGES += 3); int ThreadGuardPages = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ThreadGuardPages); if (ThreadGuardPages == 0) @@ -6495,7 +6495,7 @@ HRESULT Thread::CLRSetThreadStackGuarantee(SetThreadStackGuaranteeScope fScope) #else // HOST_64BIT #ifdef _DEBUG - uGuardSize += (1 * GetOsPageSize()); // one extra page for debug infrastructure + uGuardSize += (3 * GetOsPageSize()); // three extra pages for debug infrastructure #endif // _DEBUG #endif // HOST_64BIT @@ -7106,9 +7106,9 @@ void CheckRegDisplaySP (REGDISPLAY *pRD) if (pRD->SP && pRD->_pThread) { #ifndef NO_FIXED_STACK_LIMIT - _ASSERTE(PTR_VOID(pRD->SP) >= pRD->_pThread->GetCachedStackLimit()); + _ASSERTE(pRD->_pThread->IsExecutingOnAltStack() || PTR_VOID(pRD->SP) >= pRD->_pThread->GetCachedStackLimit()); #endif // NO_FIXED_STACK_LIMIT - _ASSERTE(PTR_VOID(pRD->SP) < pRD->_pThread->GetCachedStackBase()); + _ASSERTE(pRD->_pThread->IsExecutingOnAltStack() || PTR_VOID(pRD->SP) < pRD->_pThread->GetCachedStackBase()); } } diff --git a/src/coreclr/src/vm/threads.h b/src/coreclr/src/vm/threads.h index 1810c3ee0d8789..112a96d42134db 100644 --- a/src/coreclr/src/vm/threads.h +++ b/src/coreclr/src/vm/threads.h @@ -1035,6 +1035,8 @@ class Thread if(STSGuarantee_Force == fScope) return TRUE; + // For debug, always enable setting thread stack guarantee so that we can print the stack trace +#ifndef DEBUG //The runtime must be hosted to have escalation policy //If escalation policy is enabled but StackOverflow is not part of the policy // then we don't use SetThreadStackGuarantee @@ -1044,6 +1046,7 @@ class Thread //FAIL_StackOverflow is ProcessExit so don't use SetThreadStackGuarantee return FALSE; } +#endif // DEBUG return TRUE; } @@ -1081,7 +1084,7 @@ class Thread TS_LegalToJoin = 0x00000020, // Is it now legal to attempt a Join() - // unused = 0x00000040, + TS_ExecutingOnAltStack = 0x00000040, // Runtime is executing on an alternate stack located anywhere in the memory #ifdef FEATURE_HIJACK TS_Hijacked = 0x00000080, // Return address has been hijacked @@ -1416,6 +1419,18 @@ class Thread } #endif // DACCESS_COMPILE + DWORD IsExecutingOnAltStack() + { + LIMITED_METHOD_CONTRACT; + return (m_State & TS_ExecutingOnAltStack); + } + + void SetExecutingOnAltStack() + { + LIMITED_METHOD_CONTRACT; + FastInterlockOr((ULONG *) &m_State, TS_ExecutingOnAltStack); + } + DWORD IsBackground() { LIMITED_METHOD_CONTRACT; @@ -1841,7 +1856,7 @@ class Thread { void* curSP; curSP = (void *)GetCurrentSP(); - _ASSERTE((curSP <= m_pFrame && m_pFrame < m_CacheStackBase) || m_pFrame == (Frame*) -1); + _ASSERTE(IsExecutingOnAltStack() || (curSP <= m_pFrame && m_pFrame < m_CacheStackBase) || m_pFrame == (Frame*) -1); } #endif