Skip to content

Commit d05bbe4

Browse files
[release/7.0] Prevent unwinding through stack bottom (#81804)
* 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 (#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]>
1 parent a1136a9 commit d05bbe4

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

src/coreclr/dlls/mscoree/exports.cpp

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

2424
#define ASSERTE_ALL_BUILDS(expr) _ASSERTE_ALL_BUILDS((expr))
2525

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

@@ -194,6 +213,7 @@ extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const c
194213
// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
195214
//
196215
extern "C"
216+
NOINLINE
197217
DLLEXPORT
198218
int coreclr_initialize(
199219
const char* exePath,
@@ -212,6 +232,10 @@ int coreclr_initialize(
212232
bool hostPolicyEmbedded = false;
213233
PInvokeOverrideFn* pinvokeOverride = nullptr;
214234

235+
#ifdef TARGET_UNIX
236+
HostingApiFrameHolder apiFrameHolder(_ReturnAddress());
237+
#endif
238+
215239
ConvertConfigPropertiesToUnicode(
216240
propertyKeys,
217241
propertyValues,
@@ -420,6 +444,7 @@ int coreclr_create_delegate(
420444
// HRESULT indicating status of the operation. S_OK if the assembly was successfully executed
421445
//
422446
extern "C"
447+
NOINLINE
423448
DLLEXPORT
424449
int coreclr_execute_assembly(
425450
void* hostHandle,
@@ -435,6 +460,10 @@ int coreclr_execute_assembly(
435460
}
436461
*exitCode = -1;
437462

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

440469
ConstWStringArrayHolder argvW;

src/coreclr/vm/exceptionhandling.cpp

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

4526+
extern void* g_hostingApiReturnAddress;
4527+
45264528
//---------------------------------------------------------------------------------------
45274529
//
45284530
// This functions performs dispatching of a managed exception.
@@ -4724,7 +4726,8 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
47244726

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

4727-
if (controlPc == 0)
4729+
// Consider the exception unhandled if the unwinding cannot proceed further or if it went past the coreclr_initialize or coreclr_execute_assembly
4730+
if ((controlPc == 0) || (controlPc == (UINT_PTR)g_hostingApiReturnAddress))
47284731
{
47294732
if (!GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
47304733
{

src/coreclr/vm/stackwalk.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,8 @@ PCODE Thread::VirtualUnwindNonLeafCallFrame(T_CONTEXT* pContext, KNONVOLATILE_CO
709709
return uControlPc;
710710
}
711711

712+
extern void* g_hostingApiReturnAddress;
713+
712714
// static
713715
UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext)
714716
{
@@ -751,8 +753,9 @@ UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext)
751753

752754
uControlPc = GetIP(pContext);
753755

754-
if (uControlPc == 0)
756+
if ((uControlPc == 0) || (uControlPc == (PCODE)g_hostingApiReturnAddress))
755757
{
758+
uControlPc = 0;
756759
break;
757760
}
758761
#endif // !TARGET_UNIX

0 commit comments

Comments
 (0)