dotnet / dotnet/runtime

Request .NET 10 servicing backport of #129713 (Mono LLVM full-AOT: in-flight exception relocated by moving GC across resume unwind)

Open
#132,774 2 comments 0 reactions 0 assignees View on GitHub
area-VM-meta-mono os-ios untriaged
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Request

Please consider backporting dotnet/runtime#129713 ("[mono] Keep in-flight exception alive across LLVM resume unwind") to a .NET 10 servicing release. The fix is currently in `main` only (milestone 11.0-preview7), and I could not find an existing pull request targeting `release/10.0.x`.

### Why this matters on .NET 10

The bug produces an unrecoverable `SIGSEGV` / `EXC_BAD_ACCESS` in shipping iOS apps built with the default Release configuration for MAUI/iOS on .NET 10, where full AOT and LLVM are both enabled:

```xml

true
true

```

Debug builds are unaffected because they run under `--interpreter`, so the fault cannot be reproduced during development or on the simulator. It appears only in Release/AOT/LLVM builds — that is, in production and in store/TestFlight builds — which makes it expensive to diagnose and impossible to catch in a normal test loop.

The failure mode is not a corner case in our code. Any managed exception that unwinds through an LLVM-compiled `finally` and is then resumed for the catch search can hit it. In our app the trigger is ordinary `OperationCanceledException` propagation out of `BlockingCollection.GetConsumingEnumerable(CancellationToken)` on background consumer threads, which is a common .NET pattern. Because the in-flight exception is held as a raw pointer across the resume, a moving GC can relocate it and the resumed catch search then dereferences a stale pointer. The crash is therefore timing-dependent: it looks like a rare, unattributable native crash in the field rather than a reproducible defect.

### Observed impact

- Platform: iOS, arm64, Release + full AOT + LLVM.
- Crash signal: `SIGSEGV` / `EXC_BAD_ACCESS` at a small invalid address (e.g. `0xa40` — a field offset off a garbage `MonoImage` pointer).
- Representative symbolicated crashed-thread stack (runtime + BCL frames only; the app-level closure frame is not retained):

```text
pthread_mutex_lock
mono_os_mutex_lock
mono_image_lock
mono_image_alloc0
mono_class_setup_interfaces
ensure_inited_for_assignable_check
mono_class_is_assignable_from_general
mono_object_handle_isinst
mono_object_isinst_checked
mono_handle_exception_internal
mono_resume_unwind
mono_arm_resume_unwind
llvm_resume_unwind_trampoline
System.Collections.Concurrent.BlockingCollection`1.GetConsumingEnumerable+<>d__68.MoveNext
System.Threading.Tasks.Task.InnerInvoke
System.Threading.Tasks.Task.ExecuteEntryUnsafe
```

The second-pass catch search (`mono_object_isinst_checked`) walks a garbage `MonoClass` into lazy interface setup and faults taking the image lock — consistent frame-for-frame with the mechanism fixed in #129713. That the runtime enters `mono_class_setup_interfaces` at all for a long-initialized corlib exception type is itself evidence the exception pointer is stale.

- Reached production in release builds; the triggering exception path is a routine application lifecycle transition, so the exposure recurs for every affected user session rather than being a one-off. Aggregated crash reporting surfaces it as intermittent, unattributable native crashes spread across several signature groups.

### Build environment

- .NET SDK 10.0.302
- `ios` workload manifest 26.5.10301/10.0.100
- MAUI iOS app, Release configuration, `RunAOTCompilation=true`, `EnableLLVM=true`

### Related

- dotnet/runtime#129713 — the fix (merged to `main`, 11.0-preview7)
- dotnet/runtime#129508 — LLVM 23 FullAOT regression tracking

### Why a servicing backport rather than waiting

.NET 10 is the current LTS target for shipping MAUI/iOS apps; moving to a .NET 11 preview is not an option for store builds. Without the backport, the only mitigations available to app developers are (a) disabling LLVM in Release, which costs startup and steady-state performance, or (b) restructuring application code to avoid ever letting an exception unwind through an LLVM `finally` on a background thread — which is not generally achievable and, in our case, meant removing legitimate cancellation from a lifecycle path. Both are workarounds for a runtime defect with an already-reviewed and already-merged fix.

Happy to provide additional crash detail or to test a servicing build if that would help.

Contributor guide

Open the contributing guide

Research direction

No source file or test is named. Start by reviewing dotnet/runtime#129713 and its compatibility with release/10.0.x, then check dotnet/runtime#129508 and the reported .NET 10 iOS arm64 Release full-AOT/LLVM configuration. Done means the servicing backport is prepared and validated, or its requirements and rejection are documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, ios
Domain
compilers, mobile-dev, release
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.