Skip to content

Conversation

janvorli
Copy link
Member

@janvorli janvorli commented Aug 4, 2025

There is a problem with thread abort in funceval in case there is no managed frame on the stack between the abortion point and the FuncEvalFrame. That can happen e.g. when invoking a static method via funceval for a type with static constructor that was not invoked yet and takes a long time to complete.

The problem is caused by the fact that when EH is called to propagate the ThreadAbortException, it starts at the first managed frame and so it skips the try/catch in the funceval native code.

This change fixes it by using RaiseTheExceptionInternalOnly to raise the ThreadAbortException in the Thread::HandleThreadAbort. The Thread::HandleThreadAbort is always called by native code that has a native catch or (on Windows) ends up calling the ProcessCLRException.

I have originally made the change to call the DispatchManagedException from the Thread::HandleThreadAbort, but this issue shows it is problematic.

I have verified that the repro provided by @eterekhin in the issue report no longer causes the process to crash with failfast, but reports the funceval as timed out as expected.

Close #118015

There is a problem with threadabort in funceval in case there is no
managed frame on the stack between the abortion point and the
`FuncEvalFrame`. That can happen e.g. when invoking a static method via
funceval for a type with static constructor that was not invoked yet and
takes a long time to complete.

The problem is caused by the fact that when EH is called to propagate
the ThreadAbortException, it starts at the first managed frame and so it
skips the try/catch in the funceval native code.

This change fixes it by using `RaiseTheExceptionInternalOnly` to
raise the `ThreadAbortException` in the `Thread::HandleThreadAbort`. The
`Thread::HandleThreadAbort` is always called by native code that has a
native catch or (on Windows) ends up calling the `ProcessCLRException`.

I have originally made the change to call the `DispatchManagedException`
from the `Thread::HandleThreadAbort`, but this issue shows it is
problematic.

I have verified that the repro provided by @eterekhin in the issue
report no longer causes the process to crash with failfast, but
reports the funceval as timed out as expected.

Close dotnet#118015
@janvorli janvorli added this to the 10.0.0 milestone Aug 4, 2025
@janvorli janvorli requested a review from jkotas August 4, 2025 18:22
@janvorli janvorli self-assigned this Aug 4, 2025
@Copilot Copilot AI review requested due to automatic review settings August 4, 2025 18:22
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 a thread abort issue in function evaluation (funceval) where the process would crash with a failfast error instead of properly timing out. The issue occurred when there were no managed frames between the abortion point and the FuncEvalFrame, such as when invoking static methods whose static constructors hadn't been called yet.

Key changes:

  • Unified exception handling by removing conditional compilation around exception dispatch
  • Changed from DispatchManagedException to RaiseTheExceptionInternalOnly for all platforms

@eterekhin
Copy link
Contributor

@janvorli, Thanks a lot for the fix! I confirm the bug is gone :)

@janvorli
Copy link
Member Author

janvorli commented Aug 5, 2025

@eterekhin thank you for checking!

radekdoulik pushed a commit to radekdoulik/runtime that referenced this pull request Aug 5, 2025
There is a problem with threadabort in funceval in case there is no
managed frame on the stack between the abortion point and the
`FuncEvalFrame`. That can happen e.g. when invoking a static method via
funceval for a type with static constructor that was not invoked yet and
takes a long time to complete.

The problem is caused by the fact that when EH is called to propagate
the ThreadAbortException, it starts at the first managed frame and so it
skips the try/catch in the funceval native code.

This change fixes it by using `RaiseTheExceptionInternalOnly` to
raise the `ThreadAbortException` in the `Thread::HandleThreadAbort`. The
`Thread::HandleThreadAbort` is always called by native code that has a
native catch or (on Windows) ends up calling the `ProcessCLRException`.

I have originally made the change to call the `DispatchManagedException`
from the `Thread::HandleThreadAbort`, but this issue shows it is
problematic.

I have verified that the repro provided by @eterekhin in the issue
report no longer causes the process to crash with failfast, but
reports the funceval as timed out as expected.

Close dotnet#118015
@github-actions github-actions bot locked and limited conversation to collaborators Sep 4, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants