Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/coreclr/debug/createdump/createdumppal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ typedef BOOL (*PFN_PAL_VirtualUnwindOutOfProc)(
KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
PULONG64 functionStart,
SIZE_T baseAddress,
UnwindReadMemoryCallback readMemoryCallback);
UnwindReadMemoryCallback readMemoryCallback,
bool *isSignalFrame);

typedef BOOL (*PFN_PAL_GetUnwindInfoSize)(
SIZE_T baseAddress,
Expand Down Expand Up @@ -177,13 +178,14 @@ PAL_VirtualUnwindOutOfProc(
KNONVOLATILE_CONTEXT_POINTERS *contextPointers,
PULONG64 functionStart,
SIZE_T baseAddress,
UnwindReadMemoryCallback readMemoryCallback)
UnwindReadMemoryCallback readMemoryCallback,
bool *isSignalFrame)
{
if (!InitializePAL() || g_PAL_VirtualUnwindOutOfProc == nullptr)
{
return FALSE;
}
return g_PAL_VirtualUnwindOutOfProc(context, contextPointers, functionStart, baseAddress, readMemoryCallback);
return g_PAL_VirtualUnwindOutOfProc(context, contextPointers, functionStart, baseAddress, readMemoryCallback, isSignalFrame);
}

BOOL
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/debug/createdump/threadinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext)
uint64_t previousSp = 0;
uint64_t previousIp = 0;
int ipMatchCount = 0;
bool isSignalFrame = false;
bool crossedSignalTrampoline = 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.
Expand All @@ -69,10 +71,19 @@ ThreadInfo::UnwindNativeFrames(CONTEXT* pContext)
sp++;
}
#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.
// Allow the SP decrease if the current frame is a signal trampoline (detected by the
// previous unwind call) and we haven't already crossed one (limit to one crossing to
// bound corruption damage).
if (ip == 0 || sp == 0 || (sp <= previousSp && (!isSignalFrame || crossedSignalTrampoline))) {
TRACE_VERBOSE("Unwind: sp not increasing or ip == 0 sp %p ip %p\n", (void*)sp, (void*)ip);
break;
}
if (sp < previousSp)
{
crossedSignalTrampoline = true;
}
// Break out of the endless loop if the IP matches over a 1000 times. This is a fallback
// behavior of libunwind when the module the IP is in doesn't have unwind info and for
// simple stack overflows. The stack memory is added to the dump in GetThreadStack and
Expand Down Expand Up @@ -104,7 +115,8 @@ 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, nullptr, &functionStart, baseAddress, ReadMemoryAdapter)) {
isSignalFrame = false;
if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, &functionStart, baseAddress, ReadMemoryAdapter, &isSignalFrame)) {
TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n");
break;
}
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/debug/daccess/dacfn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ typedef BOOL(*UnwindReadMemoryCallback)(PVOID address, PVOID buffer, SIZE_T size

extern
BOOL
PAL_VirtualUnwindOutOfProc(PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback);
PAL_VirtualUnwindOutOfProc(PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, BOOL *isSignalFrame);
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cross-OS DAC minimal prototype uses BOOL* isSignalFrame, but the PAL declaration/implementation uses bool*. In C++ this changes the mangled symbol name and can break linking/calls in HOST_WINDOWS/TARGET_UNIX builds. Update the prototype to take bool* (and keep it consistent with pal.h).

Suggested change
PAL_VirtualUnwindOutOfProc(PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, BOOL *isSignalFrame);
PAL_VirtualUnwindOutOfProc(PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_POINTERS contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, bool *isSignalFrame);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like valid feedback.

#endif

HRESULT
Expand Down Expand Up @@ -282,7 +282,7 @@ DacVirtualUnwind(ULONG32 threadId, PT_CONTEXT context, PT_KNONVOLATILE_CONTEXT_P
hr = S_OK;

SIZE_T baseAddress = DacGlobalBase();
if (baseAddress == 0 || !PAL_VirtualUnwindOutOfProc(context, contextPointers, nullptr, baseAddress, DacReadAllAdapter))
if (baseAddress == 0 || !PAL_VirtualUnwindOutOfProc(context, contextPointers, nullptr, baseAddress, DacReadAllAdapter, nullptr))
{
hr = E_FAIL;
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2612,7 +2612,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, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback);
PALIMPORT BOOL PALAPI PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, 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);

Expand Down
18 changes: 16 additions & 2 deletions src/coreclr/pal/src/exception/remote-unwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2451,17 +2451,23 @@ static unw_accessors_t unwind_accessors = init_unwind_accessors();
functionStart - the pointer to return the starting address of the function or nullptr
baseAddress - base address of the module to find the unwind info
readMemoryCallback - reads memory from the target
isSignalFrame - output parameter: set to true if the unwound-to frame is a signal trampoline
--*/
BOOL
PALAPI
PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback)
PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, bool *isSignalFrame)
{
unw_addr_space_t addrSpace = 0;
unw_cursor_t cursor;
libunwindInfo info;
BOOL result = FALSE;
int st;

if (isSignalFrame)
{
*isSignalFrame = false;
}

info.BaseAddress = baseAddress;
info.Context = context;
info.FunctionStart = 0;
Expand Down Expand Up @@ -2533,6 +2539,14 @@ PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *cont
goto exit;
}

// Check if the frame we landed on is a signal trampoline. When a signal handler uses
// SA_ONSTACK, stepping from a signal frame crosses from the alternate signal stack to the
// original thread stack, which can cause the SP to decrease.
if (isSignalFrame && unw_is_signal_frame(&cursor) > 0)
{
*isSignalFrame = true;
}

UnwindContextToContext(&cursor, context);

if (contextPointers != NULL)
Expand Down Expand Up @@ -2699,7 +2713,7 @@ PAL_GetUnwindInfoSize(SIZE_T baseAddress, ULONG64 ehFrameHdrAddr, UnwindReadMemo

BOOL
PALAPI
PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback)
PAL_VirtualUnwindOutOfProc(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers, PULONG64 functionStart, SIZE_T baseAddress, UnwindReadMemoryCallback readMemoryCallback, bool *isSignalFrame)
{
return FALSE;
}
Expand Down
Loading