Description
On macOS 26.6.1 / arm64, an ASP.NET Core server reliably takes a SIGSEGV inside JIT-compiled code under
ordinary request load. The fault is converted to AccessViolationException, classified as
corrupting-state, and fail-fasted, so the process dies rather than the request failing.
The same binary and the same data do not reproduce it on macOS 15.7.9, and Linux x64 is unaffected under
considerably heavier load. Disabling tiered compilation stops it outright; disabling only dynamic PGO
reduces it by roughly two orders of magnitude without eliminating it. That combination is what makes me
think this is a tier-1 codegen problem on macOS 26 rather than anything in my code.
I do not yet have a minimal repro — see the last section.
Reproduction Steps
- Publish an ASP.NET Core (Razor Pages) app self-contained and single-file for
osx-arm64:
dotnet publish -c Release -r osx-arm64 --self-contained true \
-p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true
- Run it on macOS 26.6.1 / arm64 as a launchd daemon, backed by a PostgreSQL database with enough rows
that the page's queries do real work.
- Open a page whose panels each re-poll a fragment endpoint (
GET /dashboard?fragment=…) every 30
seconds, and leave it open.
Expected behavior
The process serves the polls.
Actual behavior
The process aborts. Measured on one host, per fragment poll:
| Setting |
Crash rate |
| default |
0.33 per poll |
DOTNET_TieredPGO=0 |
0.004 per poll |
DOTNET_TieredCompilation=0 |
none in ~400 polls |
So roughly one poll in three killed the process at defaults, and only turning tiering off held.
The native chain from a full dump:
PAL_DispatchException → HandleHardwareException → FailFastIfCorruptingStateException → PROCAbort
The managed stack points at Razor Pages model binding and QueryStringEnumerable. I do not think that is
the bug's location so much as the hottest span-heavy code on the request path, and therefore the code most
likely to have been rejitted at tier 1 by the time the fault happens. Turning tiering off means that code
is never produced, which is consistent with the rate table above.
Regression?
Not a .NET regression as far as I can tell — the OS is the variable. Same binary, same database copy, 1,600
requests on macOS 15.7.9: no crash. The only change that reproduces it is the OS version.
Known Workarounds
DOTNET_TieredCompilation=0. On a launchd daemon:
sudo plutil -replace EnvironmentVariables.DOTNET_TieredCompilation -string "0" /path/to/daemon.plist
DOTNET_TieredPGO=0 alone is not sufficient — it cuts the rate by ~80x but the process still dies.
Configuration
- .NET 10 (
net10.0), SDK 10.0.300, self-contained single-file publish
- macOS 26.6.1, arm64 (Apple silicon)
- ASP.NET Core Razor Pages, Kestrel terminating TLS, PostgreSQL via Npgsql
- Does not reproduce: macOS 15.7.9 arm64 (same binary, same data), linux-x64 (heavier load)
Other information
What I have and can supply on request: the full dump, and the managed and native stacks in more detail.
One thing worth recording for anyone else collecting one — createdump refuses anything but
DOTNET_DbgMiniDumpType=4 for a single-file app.
What I do not have is a minimal repro. The fault needs the page's queries to do real work, which in my case means a populated database; an empty one does not reproduce it. I could not reduce it to something self-contained without guessing at which part of the load matters. If a JIT triage on the stacks and the tiering correlation narrows it to a suspected method or optimisation, I am happy to build a targeted repro against that hypothesis, or to run instrumented builds or DOTNET_JitDisasm-style diagnostics on the affected host — it reproduces there within about a minute, so the feedback loop is short.
Description
On macOS 26.6.1 / arm64, an ASP.NET Core server reliably takes a SIGSEGV inside JIT-compiled code under
ordinary request load. The fault is converted to
AccessViolationException, classified ascorrupting-state, and fail-fasted, so the process dies rather than the request failing.
The same binary and the same data do not reproduce it on macOS 15.7.9, and Linux x64 is unaffected under
considerably heavier load. Disabling tiered compilation stops it outright; disabling only dynamic PGO
reduces it by roughly two orders of magnitude without eliminating it. That combination is what makes me
think this is a tier-1 codegen problem on macOS 26 rather than anything in my code.
I do not yet have a minimal repro — see the last section.
Reproduction Steps
osx-arm64:dotnet publish -c Release -r osx-arm64 --self-contained true \ -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=truethat the page's queries do real work.
GET /dashboard?fragment=…) every 30seconds, and leave it open.
Expected behavior
The process serves the polls.
Actual behavior
The process aborts. Measured on one host, per fragment poll:
DOTNET_TieredPGO=0DOTNET_TieredCompilation=0So roughly one poll in three killed the process at defaults, and only turning tiering off held.
The native chain from a full dump:
The managed stack points at Razor Pages model binding and
QueryStringEnumerable. I do not think that isthe bug's location so much as the hottest span-heavy code on the request path, and therefore the code most
likely to have been rejitted at tier 1 by the time the fault happens. Turning tiering off means that code
is never produced, which is consistent with the rate table above.
Regression?
Not a .NET regression as far as I can tell — the OS is the variable. Same binary, same database copy, 1,600
requests on macOS 15.7.9: no crash. The only change that reproduces it is the OS version.
Known Workarounds
DOTNET_TieredCompilation=0. On a launchd daemon:sudo plutil -replace EnvironmentVariables.DOTNET_TieredCompilation -string "0" /path/to/daemon.plistDOTNET_TieredPGO=0alone is not sufficient — it cuts the rate by ~80x but the process still dies.Configuration
net10.0), SDK 10.0.300, self-contained single-file publishOther information
What I have and can supply on request: the full dump, and the managed and native stacks in more detail.
One thing worth recording for anyone else collecting one —
createdumprefuses anything butDOTNET_DbgMiniDumpType=4for a single-file app.What I do not have is a minimal repro. The fault needs the page's queries to do real work, which in my case means a populated database; an empty one does not reproduce it. I could not reduce it to something self-contained without guessing at which part of the load matters. If a JIT triage on the stacks and the tiering correlation narrows it to a suspected method or optimisation, I am happy to build a targeted repro against that hypothesis, or to run instrumented builds or
DOTNET_JitDisasm-style diagnostics on the affected host — it reproduces there within about a minute, so the feedback loop is short.