Fail fast when FirstChanceExceptionEventArgs allocation fails#130505
Conversation
|
@copilot have the actual implementation got dropped from the changes somehow? |
Yes — the branch only had the empty "Initial plan" commit, so the implementation was missing. I've now pushed it. Note that since the original PR body's snippet was written, #124834 moved the So the fix moves the allocation back into the VM in EX_TRY
{
MethodTable *pMTEventArgs = CoreLibBinder::GetClass(CLASS__FIRSTCHANCE_EVENTARGS);
gc.oEventArgs = AllocateObject(pMTEventArgs);
MethodDescCallSite ctor(METHOD__FIRSTCHANCE_EVENTARGS__CTOR, &gc.oEventArgs);
ARG_SLOT ctorArgs[] = { ObjToArgSlot(gc.oEventArgs), ObjToArgSlot(gc.oThrowable) };
ctor.Call(ctorArgs);
}
EX_CATCH
{
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_OUTOFMEMORY);
UNREACHABLE();
}
EX_END_CATCHThe pre-created event args is then passed to the managed handler-dispatch path (the CoreCLR Validated on a fresh clr+libs+host build: normal flow (handler called once with non-null args) and handler-throws (nested first-chance still delivered with valid args, no spurious fail-fast) both pass. Note This comment was generated by Copilot. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/AppDomainTests.cs:211
- The test handler currently increments/throws for any first-chance exception in the RemoteExecutor process. Since first-chance notifications are process-wide (all threads), unrelated framework/runtime exceptions during the test window could make this flaky and also reduce signal.
Consider filtering to the specific exception type this test cares about (FirstChanceTestException) before incrementing/throwing, so the test only fails when recursion happens for that scenario.
EventHandler<FirstChanceExceptionEventArgs> handler = (sender, e) =>
{
count++;
throw new FirstChanceTestException("from handler");
};
src/libraries/System.Private.CoreLib/src/System/AppContext.cs:144
- The PR description/title say the runtime will fail-fast when
FirstChanceExceptionEventArgsallocation fails and will not invoke managed handlers with invalid state. This change instead introduces a per-thread reentrancy guard that silently skips nested first-chance notifications to avoid recursion/stack overflow.
Please reconcile the intended behavior: either update the PR description (and title) to match this "skip nested notifications" approach, or adjust the implementation to actually fail-fast specifically on event-args allocation failure as described.
// Guard against reentrancy. Allocating the event args below or running a
// handler may itself throw (e.g. OutOfMemoryException in a low-memory
// situation). That exception would trigger another first-chance
// notification on this same thread, allocate again, throw again, and
// recurse until the stack overflows. Skip nested notifications to break
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/AppDomainTests.cs:211
- This test handler throws for every first-chance notification after subscription, but the runtime can raise first-chance events for unrelated internal exceptions as well (see AppDomain.cs:86-90 comment about delivering for all exceptions). That makes
countpotentially > 1 and the test flaky. Consider throwing only whene.Exceptionis the specific test exception type so the test remains scoped to the behavior it is asserting.
RemoteExecutor.Invoke(() => {
int count = 0;
EventHandler<FirstChanceExceptionEventArgs> handler = (sender, e) =>
{
count++;
throw new FirstChanceTestException("from handler");
};
src/libraries/System.Private.CoreLib/src/System/AppContext.cs:149
- The PR description/title claims the runtime will fail-fast when
FirstChanceExceptionEventArgsallocation fails, but this change instead adds a managed reentrancy guard and (on CoreCLR) swallows exceptions from the callback path. As written, an allocation failure (OOM) would result in the first-chance notification being skipped, not a deterministic fail-fast. Please either update the PR description/title to match the actual behavior change, or implement the documented fail-fast behavior in the runtime dispatch path.
// Guard against reentrancy. Allocating the event args below or running a
// handler may itself throw (e.g. OutOfMemoryException in a low-memory
// situation). That exception would trigger another first-chance
// notification on this same thread, allocate again, throw again, and
// recurse until the stack overflows. Skip nested notifications to break
// the recursion.
if (t_deliveringFirstChanceNotification)
{
return;
}
Allocate the FirstChanceExceptionEventArgs in the VM during first-chance dispatch and fail fast if the allocation fails, instead of allocating in managed code (which would recurse to a stack overflow on OOM) or passing a null event args to handlers. Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
… MethodDescCallSite Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
…ocation Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
c8f7da9 to
5f4fd08
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/libraries/System.Private.CoreLib/src/System/AppContext.cs:149
- The PR description/title says allocation failure of FirstChanceExceptionEventArgs should fail-fast (and implies the allocation happens in the VM), but this change instead adds a managed reentrancy guard that suppresses nested first-chance notifications and continues execution. If the intended behavior is now “skip nested notifications to avoid recursion”, the PR metadata should be updated; if the intended behavior is truly fail-fast on args allocation failure, this method would need an explicit fatal path (e.g., fail-fast on OutOfMemoryException from
new FirstChanceExceptionEventArgs(e)), which is not present here.
// Guard against reentrancy. Allocating the event args below or running a
// handler may itself throw (e.g. OutOfMemoryException in a low-memory
// situation). That exception would trigger another first-chance
// notification on this same thread, allocate again, throw again, and
// recurse until the stack overflows. Skip nested notifications to break
// the recursion.
if (t_deliveringFirstChanceNotification)
{
return;
}
|
@copilot there are test failures on mono. I am not sure we want to change behavior of mono for this, so make sure the added tests do not run on mono. |
Co-authored-by: VSadov <8218165+VSadov@users.noreply.github.com>
Added Note This comment was generated by Copilot. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/libraries/System.Private.CoreLib/src/System/AppContext.cs:155
- The PR title/description say we should fail-fast when allocating FirstChanceExceptionEventArgs fails (and mentions a VM AllocateObject(...) null check). In the current implementation, args allocation is performed via
new(e)and any resulting OutOfMemoryException will be swallowed by the CoreCLR UnmanagedCallersOnly boundary, while nested first-chance notifications are simply skipped via the thread-static guard. Please either (a) update the PR description/title to match the implemented behavior (reentrancy suppression + swallowing callback exceptions), or (b) add an explicit fail-fast on allocation failure (e.g., catch OutOfMemoryException aroundnew(e)and terminate) to match the stated intent.
t_deliveringFirstChanceNotification = true;
try
{
FirstChanceExceptionEventArgs args = new(e);
main PR N/A
Description
First-chance exception dispatch could invoke managed handlers with a null
FirstChanceExceptionEventArgswhen allocation failed, which can recurse and end in stack overflow. This change makes that allocation-failure path terminate via fail-fast instead of calling into managed handlers with invalid state.Behavior change
FirstChanceExceptionEventArgsfails, runtime now fail-fasts immediately.AppDomain.FirstChanceExceptionhandlers with a null event args instance.Implementation
FirstChanceExceptionEventArgsallocation failure as fatal.Customer Impact
Prevents unbounded recursion/stack overflow in low-memory exception paths and replaces it with deterministic fail-fast behavior.
Regression
Not identified as a recent regression; this addresses a longstanding edge case in an OOM path.
Testing
Covered by existing first-chance exception behavior tests for normal flow; this change is isolated to the fatal allocation-failure branch.
Risk
Low. Change is narrowly scoped to an already-fatal low-memory edge path and does not affect normal exception dispatch semantics.
Package authoring no longer needed in .NET 9
IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.