Skip to content

Fix IP adjustment for interpreter EH #117055

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

janvorli
Copy link
Member

The exception handling stack frame iterator adjusts IP it returns for all cases when the IP represents a return address. This is to make sure that if the last instruction in a try regios is a call, it is still covered by the try region (the return address would be right after it). For jit/aoted code, throwing a software exceptions results in a call. Only hardware exceptions like division by zero etc report the address of the failing instruction.
In the interpreter, when an exception is thrown, the IP address is always the address of the instruction that has triggered the exception. So we should not adjust the IP.
This change fixes that by marking exception throwing frame as "faulting", which ensures the adjustment doesn't occur. That information is recorded in the InterpreterFrame when an exception is thrown by the interpreter code and cleared when the execution resumes after catch.

The exception handling stack frame iterator adjusts IP it returns for
all cases when the IP represents a return address. This is to make sure
that if the last instruction in a try regios is a call, it is still covered
by the try region (the return address would be right after it).
For jit/aoted code, throwing a software exceptions results in a call.
Only hardware exceptions like division by zero etc report the address of
the failing instruction.
In the interpreter, when an exception is thrown, the IP address is
always the address of the instruction that has triggered the exception.
So we should not adjust the IP.
This change fixes that by marking exception throwing frame as
"faulting", which ensures the adjustment doesn't occur.
That information is recorded in the InterpreterFrame when an
exception is thrown by the interpreter code and cleared when the
execution resumes after catch.
@janvorli janvorli added this to the 10.0.0 milestone Jun 26, 2025
@janvorli janvorli self-assigned this Jun 26, 2025
@Copilot Copilot AI review requested due to automatic review settings June 26, 2025 15:06
@janvorli janvorli requested a review from BrzVlad as a code owner June 26, 2025 15:06
@janvorli janvorli requested a review from kg as a code owner June 26, 2025 15:06
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes the IP adjustment for interpreter exception handling by marking interpreter frames as faulting when an exception is thrown, ensuring the IP remains unadjusted during interpreter exceptions. Key changes include:

  • Setting faulting flags in stackwalk.cpp when an exception context is detected.
  • Updating interpexec.cpp to mark interpreter frames as faulting during exception throw and rethrow.
  • Introducing a new faulting flag and related setter in InterpreterFrame (frames.h and frames.cpp) and applying it in excep.cpp.

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/vm/stackwalk.cpp Added faulting flag logic to set exception active context status.
src/coreclr/vm/interpexec.cpp Marked interpreter frames as faulting for throw and rethrow paths.
src/coreclr/vm/frames.h Added the m_isFaulting field and SetIsFaulting method to InterpreterFrame.
src/coreclr/vm/frames.cpp Updated context setting to reflect the faulting status.
src/coreclr/vm/excep.cpp Ensured interpreter frames are marked as faulting during unwind.
Comments suppressed due to low confidence (1)

src/coreclr/vm/stackwalk.cpp:2863

  • Consider adding a brief comment explaining why checking for CONTEXT_EXCEPTION_ACTIVE here leads to setting isInterrupted and hasFaulted. This will help maintainability by clarifying the relationship between the context flag and the interpreter frame state.
                    if (pRD->pCurrentContext->ContextFlags & CONTEXT_EXCEPTION_ACTIVE)

Copy link
Contributor

Tagging subscribers to this area: @BrzVlad, @janvorli, @kg
See info in area-owners.md if you want to be subscribed.

@@ -7360,6 +7360,12 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra
}
else
{
#ifdef FEATURE_INTERPRETER
if ((pEntryFrame != NULL) && (pEntryFrame->GetFrameIdentifier() == FrameIdentifier::InterpreterFrame))
Copy link
Member

Choose a reason for hiding this comment

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

Does both of us overlooking 'pEntryFrame can be NULL' in this case indicate that we need a comment in a key spot to make it clear in the future that it's potentially null?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've actually just pushed another fix, the NULL was not a correct fix. The correct check is FRAME_TOP. The Thread::GetFrame() that is a source of this pEntryFrame returns that when there are no frames on the thread.

pContext->ContextFlags = CONTEXT_FULL;
if (m_isFaulting)
{
pContext->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
Copy link
Member

@BrzVlad BrzVlad Jun 27, 2025

Choose a reason for hiding this comment

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

How does exactly setting this flag end up preventing the ip adjustment ? Seems not obvious from searching in the code.

Copy link
Member Author

Choose a reason for hiding this comment

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

The EH doesn't do adjustment for frames that are marked as faulting. In the JIT/AOT case, these are frames of hardware exceptions where the IP is the IP of the faulting instruction. For the interpreter, the throwing IR opcodes behave the same way. Setting this flag results in setting the m_crawl.isFaulting and m_crawl.isInterrupted to true. The SfiInit and SfiNext perform the IP adjustment only when the isFaulting is not set.
E.g. for the SfiInit, you can see it here:

if (!pThis->m_crawl.HasFaulted() && !pThis->m_crawl.IsIPadjusted())
{
controlPC -= STACKWALK_CONTROLPC_ADJUST_OFFSET;
}

@@ -1737,12 +1737,14 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
{
throwable = LOCAL_VAR(ip[1], OBJECTREF);
}
pInterpreterFrame->SetIsFaulting(true);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this flag also be set for the other sources of exception throwing in the interp method execution ? Conv ovf opcodes for example

Copy link
Member Author

Choose a reason for hiding this comment

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

No, these are all handled in the UnwindAndContinueRethrowHelperAfterCatch

Copy link
Member Author

Choose a reason for hiding this comment

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

That one is called in the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER macro in the InterpExecMethod.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants