Skip to content

x86/funclets: Handle longjmp #115932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 24, 2025
Merged
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
55 changes: 53 additions & 2 deletions src/coreclr/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,40 @@ static void PopExplicitFrames(Thread *pThread, void *targetSp, void *targetCalle
}
}

#if defined(HOST_WINDOWS) && defined(HOST_X86)
static void DispatchLongJmp(IN PEXCEPTION_RECORD pExceptionRecord,
IN PVOID pEstablisherFrame,
IN OUT PCONTEXT pContextRecord)
{
// Pop all the SEH frames including the one that is currently called
// to prevent setjmp from trying to unwind it again when we inject our
// longjmp.
SetCurrentSEHRecord(((PEXCEPTION_REGISTRATION_RECORD)pEstablisherFrame)->Next);

#pragma warning(push)
#pragma warning(disable:4611) // interaction between 'function' and C++ object destruction is non-portable
jmp_buf jumpBuffer;
if (setjmp(jumpBuffer))
{
// We reach this point after the finally handlers were called and
// the unwinding should continue below the managed frame.
return;
}
#pragma warning(pop)

// Emulate the parameters that are passed on 64-bit systems and expected by
// DispatchManagedException.
EXCEPTION_RECORD newExceptionRecord = *pExceptionRecord;
newExceptionRecord.NumberParameters = 2;
newExceptionRecord.ExceptionInformation[0] = (DWORD_PTR)&jumpBuffer;
newExceptionRecord.ExceptionInformation[1] = 1;

OBJECTREF oref = ExInfo::CreateThrowable(&newExceptionRecord, FALSE);
DispatchManagedException(oref, pContextRecord, &newExceptionRecord);
UNREACHABLE();
}
#endif

EXTERN_C EXCEPTION_DISPOSITION __cdecl
ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord,
IN PVOID pEstablisherFrame,
Expand Down Expand Up @@ -613,14 +647,22 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord,
}

// Second pass (unwinding)
GCX_COOP();
GCX_COOP_NO_DTOR();
ThreadExceptionState* pExState = pThread->GetExceptionState();
ExInfo *pPrevExInfo = (ExInfo*)pExState->GetCurrentExceptionTracker();
if (pPrevExInfo != NULL && pPrevExInfo->m_DebuggerExState.GetDebuggerInterceptContext() != NULL)
{
ContinueExceptionInterceptionUnwind();
UNREACHABLE();
}
#if defined(HOST_WINDOWS) && defined(HOST_X86)
else if (pExceptionRecord->ExceptionCode == STATUS_LONGJUMP)
{
DispatchLongJmp(pExceptionRecord, pEstablisherFrame, pContextRecord);
GCX_COOP_NO_DTOR_END();
return ExceptionContinueSearch;
}
#endif
else
{
OBJECTREF oref = ExInfo::CreateThrowable(pExceptionRecord, FALSE);
Expand Down Expand Up @@ -3030,10 +3072,12 @@ void ExecuteFunctionBelowContext(PCODE functionPtr, CONTEXT *pContext, size_t ta
}

#ifdef HOST_WINDOWS
VOID DECLSPEC_NORETURN PropagateLongJmpThroughNativeFrames(jmp_buf *pJmpBuf, int retVal)
VOID DECLSPEC_NORETURN __fastcall PropagateLongJmpThroughNativeFrames(jmp_buf *pJmpBuf, int retVal)
{
WRAPPER_NO_CONTRACT;
GCX_PREEMP_NO_DTOR();
longjmp(*pJmpBuf, retVal);
UNREACHABLE();
}

// This is a personality routine that the RtlRestoreContext calls when it is called with
Expand Down Expand Up @@ -3235,7 +3279,14 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio
if (pLongJmpBuf != NULL)
{
STRESS_LOG2(LF_EH, LL_INFO100, "Resuming propagation of longjmp through native frames at IP=%p, SP=%p\n", GetIP(pvRegDisplay->pCurrentContext), GetSP(pvRegDisplay->pCurrentContext));
#ifdef HOST_X86
// On x86 we don't jump to the original longjmp target. Instead we return to DispatchLongJmp called
// from ProcessCLRException which in turn return ExceptionContinueSearch to continue unwinding the
// original longjmp call.
longjmp(*pLongJmpBuf, longJmpReturnValue);
#else
ExecuteFunctionBelowContext((PCODE)PropagateLongJmpThroughNativeFrames, pvRegDisplay->pCurrentContext, targetSSP, (size_t)pLongJmpBuf, longJmpReturnValue);
#endif
}
else
#endif
Expand Down
Loading