diff --git a/src/coreclr/debug/createdump/createdumppal.cpp b/src/coreclr/debug/createdump/createdumppal.cpp index 94e4606ea0fb9e..a28b35954932e6 100644 --- a/src/coreclr/debug/createdump/createdumppal.cpp +++ b/src/coreclr/debug/createdump/createdumppal.cpp @@ -23,7 +23,8 @@ typedef BOOL (*PFN_PAL_VirtualUnwindOutOfProc)( CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, - UnwindReadMemoryCallback readMemoryCallback); + UnwindReadMemoryCallback readMemoryCallback, + BOOL *isSignalFrame); typedef BOOL (*PFN_PAL_GetUnwindInfoSize)( SIZE_T baseAddress, @@ -129,13 +130,14 @@ PAL_VirtualUnwindOutOfProc( CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, - UnwindReadMemoryCallback readMemoryCallback) + UnwindReadMemoryCallback readMemoryCallback, + BOOL *isSignalFrame) { if (!InitializePAL() || g_PAL_VirtualUnwindOutOfProc == nullptr) { return FALSE; } - return g_PAL_VirtualUnwindOutOfProc(context, functionStart, baseAddress, readMemoryCallback); + return g_PAL_VirtualUnwindOutOfProc(context, functionStart, baseAddress, readMemoryCallback, isSignalFrame); } BOOL diff --git a/src/coreclr/debug/createdump/threadinfo.cpp b/src/coreclr/debug/createdump/threadinfo.cpp index 75313e422d96a9..4ca93b50db3d5a 100644 --- a/src/coreclr/debug/createdump/threadinfo.cpp +++ b/src/coreclr/debug/createdump/threadinfo.cpp @@ -51,6 +51,7 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) uint64_t previousSp = 0; uint64_t previousIp = 0; int ipMatchCount = 0; + bool previousFrameWasSignal = false; // For each native frame, add a page around the IP and any unwind info not already // added in VisitProgramHeader (Linux) and VisitSection (MacOS) to the dump. @@ -70,6 +71,18 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) } #endif if (ip == 0 || sp <= previousSp) { + // When a signal handler uses SA_ONSTACK (alternate signal stack), the SP can legitimately + // decrease when unwinding crosses the signal trampoline back to the original thread stack. + // This commonly happens on secondary threads where both the thread stack and alt stack are + // mmap'd, with the alt stack at a higher address. Allow one SP decrease if the previous + // frame was a signal trampoline. + if (ip != 0 && sp != 0 && sp < previousSp && previousFrameWasSignal) { + TRACE("Unwind: signal trampoline crossing detected sp %p -> %p\n", (void*)previousSp, (void*)sp); + previousSp = sp - 1; + previousIp = ip; + previousFrameWasSignal = false; + continue; + } TRACE_VERBOSE("Unwind: sp not increasing or ip == 0 sp %p ip %p\n", (void*)sp, (void*)ip); break; } @@ -104,10 +117,12 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext) // Unwind the native frame adding all the memory accessed to the core dump via the read memory adapter. ULONG64 functionStart; - if (!PAL_VirtualUnwindOutOfProc(pContext, &functionStart, baseAddress, ReadMemoryAdapter)) { + BOOL isSignalFrame = FALSE; + if (!PAL_VirtualUnwindOutOfProc(pContext, &functionStart, baseAddress, ReadMemoryAdapter, &isSignalFrame)) { TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n"); break; } + previousFrameWasSignal = (isSignalFrame != FALSE); if (m_crashInfo.GatherFrames()) { diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 4c1b089882eccf..d12791c71f1db3 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -2494,7 +2494,7 @@ typedef BOOL(*UnwindReadMemoryCallback)(PVOID address, PVOID buffer, SIZE_T size PALIMPORT BOOL PALAPI PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers); -PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback); +PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, BOOL *isSignalFrame); PALIMPORT BOOL PALAPI PAL_GetUnwindInfoSize(SIZE_T baseAddress, ULONG64 ehFrameHdrAddr, UnwindReadMemoryCallback readMemoryCallback, PULONG64 ehFrameStart, PULONG64 ehFrameSize); diff --git a/src/coreclr/pal/src/exception/remote-unwind.cpp b/src/coreclr/pal/src/exception/remote-unwind.cpp index 1208ed112b8980..0841500bda8356 100644 --- a/src/coreclr/pal/src/exception/remote-unwind.cpp +++ b/src/coreclr/pal/src/exception/remote-unwind.cpp @@ -2337,7 +2337,7 @@ static unw_accessors_t unwind_accessors = init_unwind_accessors(); --*/ BOOL PALAPI -PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) +PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, BOOL *isSignalFrame) { unw_addr_space_t addrSpace = 0; unw_cursor_t cursor; @@ -2345,6 +2345,11 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T base BOOL result = FALSE; int st; + if (isSignalFrame) + { + *isSignalFrame = FALSE; + } + info.BaseAddress = baseAddress; info.Context = context; info.FunctionStart = 0; @@ -2409,6 +2414,20 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T base goto exit; } + // Check if the current frame is a signal trampoline before stepping. When the signal handler + // uses SA_ONSTACK, stepping past a signal frame crosses from the alternate signal stack to the + // original thread stack, which can cause the SP to decrease. + // Note: unw_is_signal_frame requires the cursor's proc_info to be populated. unw_init_remote + // does not call find_proc_info, so we force it via unw_get_proc_info first. + if (isSignalFrame) + { + unw_proc_info_t procInfo; + if (unw_get_proc_info(&cursor, &procInfo) == 0 && unw_is_signal_frame(&cursor) > 0) + { + *isSignalFrame = TRUE; + } + } + st = unw_step(&cursor); if (st < 0) { @@ -2578,7 +2597,7 @@ PAL_GetUnwindInfoSize(SIZE_T baseAddress, ULONG64 ehFrameHdrAddr, UnwindReadMemo BOOL PALAPI -PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback) +PAL_VirtualUnwindOutOfProc(CONTEXT *context, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, BOOL *isSignalFrame) { return FALSE; }