Skip to content

Commit b14cd9b

Browse files
github-actions[bot]janvorlicarlossanlop
authored
[release/6.0] Prevent unwinding through stack bottom (dotnet#81805)
* Prevent unwinding through stack bottom When processing unhandled exception on the most recent Alpine 3.17, the libunwind doesn't stop at the bottom frame of the main thread (the caller of `main`) and tries to unwind further. The reason is that the method is missing dwarf unwind information, so the libunwind falls back to using RBP chain, but the RBP points to a garbage and so it ends up crashing with SIGSEGV. While the missing DWARF unwind info seems to be a bug in the Alpine 3.17 (older ones work fine), we can prevent issues like this by stopping at the hosting API boundary and not trying to unwind past that. This is what this PR does. * Fix bug introduced by preventing unwind through stack bottom (dotnet#81956) The irecent fix to prevent unwinding through stack bottom was incorrect for secondary threads, as it just compared the SP being above the frame of the hosting API. However, other threads can have their stacks anywhere in the memory, thus this sometimes broke exception handling on secondary threads. I have also found that there was one more case where the unwind through the hosting API need to be checked - the Thread::VirtualUnwindToFirstManagedCallFrame. I have decided to use the return address of the hosting API for the checks instead of the frame address. That makes the check work properly. --------- Co-authored-by: Jan Vorlicek <[email protected]> Co-authored-by: Carlos Sanchez <[email protected]>
1 parent bd34800 commit b14cd9b

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

src/coreclr/dlls/mscoree/unixinterface.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@
2222

2323
#define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS(__FILE__, (expr))
2424

25+
#ifdef TARGET_UNIX
26+
#define NO_HOSTING_API_RETURN_ADDRESS ((void*)ULONG_PTR_MAX)
27+
void* g_hostingApiReturnAddress = NO_HOSTING_API_RETURN_ADDRESS;
28+
29+
class HostingApiFrameHolder
30+
{
31+
public:
32+
HostingApiFrameHolder(void* returnAddress)
33+
{
34+
g_hostingApiReturnAddress = returnAddress;
35+
}
36+
37+
~HostingApiFrameHolder()
38+
{
39+
g_hostingApiReturnAddress = NO_HOSTING_API_RETURN_ADDRESS;
40+
}
41+
};
42+
#endif // TARGET_UNIX
43+
2544
// Holder for const wide strings
2645
typedef NewArrayHolder<const WCHAR> ConstWStringHolder;
2746

@@ -179,6 +198,7 @@ extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const c
179198
// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
180199
//
181200
extern "C"
201+
NOINLINE
182202
DLLEXPORT
183203
int coreclr_initialize(
184204
const char* exePath,
@@ -197,6 +217,10 @@ int coreclr_initialize(
197217
bool hostPolicyEmbedded = false;
198218
PInvokeOverrideFn* pinvokeOverride = nullptr;
199219

220+
#ifdef TARGET_UNIX
221+
HostingApiFrameHolder apiFrameHolder(_ReturnAddress());
222+
#endif
223+
200224
ConvertConfigPropertiesToUnicode(
201225
propertyKeys,
202226
propertyValues,
@@ -421,6 +445,7 @@ int coreclr_create_delegate(
421445
// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
422446
//
423447
extern "C"
448+
NOINLINE
424449
DLLEXPORT
425450
int coreclr_execute_assembly(
426451
void* hostHandle,
@@ -436,6 +461,10 @@ int coreclr_execute_assembly(
436461
}
437462
*exitCode = -1;
438463

464+
#ifdef TARGET_UNIX
465+
HostingApiFrameHolder apiFrameHolder(_ReturnAddress());
466+
#endif
467+
439468
ICLRRuntimeHost4* host = reinterpret_cast<ICLRRuntimeHost4*>(hostHandle);
440469

441470
ConstWStringArrayHolder argvW;

src/coreclr/vm/exceptionhandling.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4518,6 +4518,8 @@ VOID UnwindManagedExceptionPass2(PAL_SEHException& ex, CONTEXT* unwindStartConte
45184518
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
45194519
}
45204520

4521+
extern void* g_hostingApiReturnAddress;
4522+
45214523
//---------------------------------------------------------------------------------------
45224524
//
45234525
// This functions performs dispatching of a managed exception.
@@ -4713,7 +4715,8 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
47134715

47144716
STRESS_LOG2(LF_EH, LL_INFO100, "Processing exception at native frame: IP = %p, SP = %p \n", controlPc, sp);
47154717

4716-
if (controlPc == 0)
4718+
// Consider the exception unhandled if the unwinding cannot proceed further or if it went past the coreclr_initialize or coreclr_execute_assembly
4719+
if ((controlPc == 0) || (controlPc == (UINT_PTR)g_hostingApiReturnAddress))
47174720
{
47184721
if (!GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
47194722
{

src/coreclr/vm/stackwalk.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,8 @@ PCODE Thread::VirtualUnwindNonLeafCallFrame(T_CONTEXT* pContext, KNONVOLATILE_CO
697697
return uControlPc;
698698
}
699699

700+
extern void* g_hostingApiReturnAddress;
701+
700702
// static
701703
UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext)
702704
{
@@ -739,8 +741,9 @@ UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext)
739741

740742
uControlPc = GetIP(pContext);
741743

742-
if (uControlPc == 0)
744+
if ((uControlPc == 0) || (uControlPc == (PCODE)g_hostingApiReturnAddress))
743745
{
746+
uControlPc = 0;
744747
break;
745748
}
746749
#endif // !TARGET_UNIX

0 commit comments

Comments
 (0)