Skip to content
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
Fix edit and continue in VS with hardware exceptions
There is a subtle bug in hardware exception handling in the runtime
that causes the debugged process to crash when a user tries to use the
"unwind to this frame" on the frame where the exception happened.
This issue was introduced by the recent changes that modified hardware
exception handling.

The problem turned out to be in how we update the exception and context
records in the HandleManagedFaultFilter when we re-raise the exception
using RaiseException from the VEH. Updating the context is both not
necessary and wrong. All we need to update is the `ExceptionAddress` in
the `EXCEPTION_RECORD`, since the RaiseException puts the address of
itself there and we need the address of the original access violation to
be there.
The context should stay untouched as we are unwinding from the
RaiseException.

The context copying was originally made to mimick the code in the
`NakedThrowHelper` used before the exception handling change. That one
needed the context update, as it was used to fix unwinding over the
NakedThrowHelper that was a hijack helper. In the current state, nothing
like that is needed.

With this fix, the VS debugger works as expected.
  • Loading branch information
janvorli committed Sep 29, 2022
commit d75309d21240d24f30fdca35c8e3c916d0fa1505
25 changes: 3 additions & 22 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6526,32 +6526,13 @@ AdjustContextForJITHelpers(

#if defined(USE_FEF) && !defined(TARGET_UNIX)

static void FixContextForFaultingExceptionFrame(
EXCEPTION_POINTERS* ep,
EXCEPTION_RECORD* pOriginalExceptionRecord,
CONTEXT* pOriginalExceptionContext)
{
WRAPPER_NO_CONTRACT;

// don't copy param args as have already supplied them on the throw
memcpy((void*) ep->ExceptionRecord,
(void*) pOriginalExceptionRecord,
offsetof(EXCEPTION_RECORD, ExceptionInformation)
);

ReplaceExceptionContextRecord(ep->ContextRecord, pOriginalExceptionContext);

GetThread()->ResetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);
}

struct HandleManagedFaultFilterParam
{
// It's possible for our filter to be called more than once if some other first-pass
// handler lets an exception out. We need to make sure we only fix the context for
// the first exception we see. This flag takes care of that.
BOOL fFilterExecuted;
EXCEPTION_RECORD *pOriginalExceptionRecord;
CONTEXT *pOriginalExceptionContext;
};

static LONG HandleManagedFaultFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
Expand All @@ -6562,7 +6543,8 @@ static LONG HandleManagedFaultFilter(EXCEPTION_POINTERS* ep, LPVOID pv)

if (!pParam->fFilterExecuted)
{
FixContextForFaultingExceptionFrame(ep, pParam->pOriginalExceptionRecord, pParam->pOriginalExceptionContext);
ep->ExceptionRecord->ExceptionAddress = pParam->pOriginalExceptionRecord->ExceptionAddress;
GetThread()->ResetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);
pParam->fFilterExecuted = TRUE;
}

Expand All @@ -6584,15 +6566,14 @@ void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord, CONTEXT* pContext)
HandleManagedFaultFilterParam param;
param.fFilterExecuted = FALSE;
param.pOriginalExceptionRecord = pExceptionRecord;
param.pOriginalExceptionContext = pContext;

PAL_TRY(HandleManagedFaultFilterParam *, pParam, &param)
{
GetThread()->SetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);

EXCEPTION_RECORD *pRecord = pParam->pOriginalExceptionRecord;

RaiseException(pRecord->ExceptionCode, pRecord->ExceptionFlags,
RaiseException(pRecord->ExceptionCode, 0,
pRecord->NumberParameters, pRecord->ExceptionInformation);
}
PAL_EXCEPT_FILTER(HandleManagedFaultFilter)
Expand Down