diff --git a/src/coreclr/src/debug/ee/controller.cpp b/src/coreclr/src/debug/ee/controller.cpp index 03b27612e5a5e..137fcf774067d 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 4037e51cd0ad3..21ce9d3d4b0ec 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 28232251b1318..61eba90008d95 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 eac3b29b81df8..9c6dda1f2fb7b 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 b52e8a64d19af..524bd11b364f8 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 8e23099193028..16e527a1ebcb8 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 ef95f5e354a3d..e0ebff145b893 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 49fdec8db89e4..d6d8256610e5c 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 51b098809447a..7b5e1d71407b1 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 c8dcc43d06f56..1473601c51252 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 479a1e0fd08ab..c970326273297 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 171e5a2c769e9..54c0019a944c6 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 57f7b9ed9adf7..d5c9eaec92377 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 68e6f08a035f5..34fb7235ff2f5 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 1df5420770a80..89cb80f00d260 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 f00c4509ec829..2d54b5445d178 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 2c5580a0d3682..dc5a85c8df2f6 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 0d77f0b8fb500..036aeb9ff493a 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 e1de20a54b25c..075737e0f8a34 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 3394a3fc01c4f..35a39f359cecc 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 7f9aba1b30995..b6fb51242dc1f 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 827637b53a30a..f48f36a20391b 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 @@ -5277,6 +5284,7 @@ BOOL HandleHardwareException(PAL_SEHException* ex) fef.InitAndLink(ex->GetContextRecord()); } + SaveCurrentExceptionInfo(ex->GetExceptionRecord(), ex->GetContextRecord()); DispatchManagedException(*ex, true /* isHardwareException */); UNREACHABLE(); } diff --git a/src/coreclr/src/vm/frames.cpp b/src/coreclr/src/vm/frames.cpp index fafc7d3d68117..9210323fcc5ea 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 49a5aa16307ed..86a25f98d5063 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 14c3975523830..3eec4408cfab4 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 0251b05724f8c..66890ec1002a6 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 da222bc10253f..f9586156b7b55 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 1810c3ee0d878..112a96d42134d 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