Repository navigation
Assertion 'codeManager->IsUnwindable(pvAddress) || runtime->IsConservativeStackReportingEnabled()' in System.Runtime.Tests on ARM64 #134912
Description
To pull down the dump: runfo get-helix-payload -j a698b81e-cb53-4dfe-8b42-f182446f56ba -w System.Runtime.Tests -o c:\helix\a698b81e-cb53-4dfe-8b42-f182446f56ba\System.Runtime.Tests\
chmod +rwx System.Runtime.Tests ^&^& ./System.Runtime.Tests -notrait category=AdditionalTimezoneChecks -notrait category=IgnoreForCI -notrait category=OuterLoop -notrait category=failing -xml testResults.xml
popd
===========================================================================================================
/root/helix/work/workitem/e /root/helix/work/workitem/e
Running assembly:System.Runtime.Tests, Version=11.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
[SKIP] System.Text.Tests.CompositeFormatTests.DebuggerDisplay_ShowsFormat
[SKIP] System.Tests.UIntPtrTests.BigMul32
[SKIP] System.Text.Tests.EncodingTests.GetEncoding_FromProvider_ByCodePage_WithDisallowedEncoding_Throws
[SKIP] System.Text.Tests.EncodingTests.GetEncoding_FromProvider_ByEncodingName_WithDisallowedEncoding_Throws
[SKIP] System.Text.Tests.EncodingTests.GetEncodings_FromProvider_DoesNotContainDisallowedEncodings
[SKIP] System.Text.Tests.EncodingTests.RegisterProvider_EncodingsAreUsable
[SKIP] System.Tests.ActivatorTests.CreateInstanceAssemblyResolve
[SKIP] System.Tests.ActivatorTests.TestingCreateInstanceObjectHandle
[SKIP] System.Tests.ActivatorTests.CreateInstance_NonPublicValueTypeWithPrivateDefaultConstructor_Success
[SKIP] System.Tests.ActivatorTests.TestingBindingFlags
[SKIP] System.Tests.ActivatorTests.TestingCreateInstanceObjectHandleFullSignature
[SKIP] System.Tests.ActivatorTests.CreateInstance_TypeBuilder_ThrowsNotSupportedException
[SKIP] System.Tests.ActivatorTests.TestingCreateInstanceFromObjectHandleFullSignature
[SKIP] System.Tests.ActivatorTests.CreateInstance_PublicOnlyValueTypeWithPrivateDefaultConstructor_ThrowsMissingMethodException
[SKIP] System.Tests.ActivatorTests.TestingCreateInstanceFromObjectHandle
[SKIP] System.Tests.TypeTestsExtended.GetTypeByName_NoSuchType_ThrowsTypeLoadException
[SKIP] System.Tests.TypeTestsExtended.GetTypeByNameCaseSensitiveTypeloadFailure
[SKIP] System.Tests.TypeTestsExtended.MakeGenericType_NonRuntimeType
[SKIP] System.Tests.TypeTestsExtended.GetTypeByName
[SKIP] System.Reflection.Tests.MethodBaseTests.TestMethodBody
[SKIP] System.Tests.GCExtendedTests.GetGCMemoryInfo
--------------------------------------------------
Debug Assertion Violation
Expression: 'codeManager->IsUnwindable(pvAddress) || runtime->IsConservativeStackReportingEnabled()'
File: /__w/1/s/src/coreclr/nativeaot/Runtime/thread.cpp, Line: 709
--------------------------------------------------
Known Issue Error Message
DO NOT USE JSON BELOW IF THIS IS A BUILD BREAK otherwise build analysis will allow pull requests to merge that break the build worse. For a build break, do not use this issue form. Make a regular new issue.
Fill the error message using step by step known issues guidance.
{
"ErrorMessage": "'codeManager->IsUnwindable(pvAddress) || runtime->IsConservativeStackReportingEnabled()'",
"ErrorPattern": "",
"BuildRetry": false,
"ExcludeConsoleLog": false
}Known issue validation
Build: 🔎 https://dev.azure.com/dnceng-public/public/_build/results?buildId=1615918
Error message validated: ['codeManager->IsUnwindable(pvAddress) || runtime->IsConservativeStackReportingEnabled()']
Result validation: ✅ Known issue matched with the provided build.
Validation performed at: 10/4/2026 6:06:47 PM UTC
Report
| Build | Repository | Test | Pull Request |
|---|---|---|---|
| 1622350 | dotnet/runtime | System.IO.FileSystem.Tests.WorkItemExecution | #135172 |
| 1615918 | dotnet/runtime | System.Runtime.Tests.WorkItemExecution |
Summary
| 24-Hour Hit Count | 7-Day Hit Count | 1-Month Count |
|---|---|---|
| 0 | 2 | 2 |
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
dotnet-policy-service commentedon Sep 30, 2026
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
See info in area-owners.md if you want to be subscribed.
MichalStrehovsky commentedon Sep 30, 2026
Root cause: ARM64
stackallocfalsely classified as an epilogThe assertion is caused by a false-negative unwindability classification in NativeAOT’s ARM64 epilog detector. A legitimate stack-allocation instruction is mistaken for an epilog instruction. The interrupted location is marked GC-interruptible, so this produces the contradiction asserted in
Thread::HijackCallback.Investigated artifacts
49e04fa0e472453f4391c4a7097d9e55380dbef2System.Runtime.Tests, Linux ARM64 NativeAOTSystem.Runtime.Tests.dbgcore.1000.1789b89ce46b63f335c7a0779a423d40ee35b33b88All source locations below refer to that checkout.
1. Interrupted instruction and its purpose
The aborting thread is OS thread 26. Its relevant stack is:
Another thread is in
ThreadStore::SuspendAllThreads/GCToEEInterface::SuspendEE.The interrupted PC is:
Relevant instructions, with offsets relative to the function start:
This corresponds to:
in
src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs:188.The
ldpprobes the stack and decreases SP by 512 bytes, allocating the buffer. It does not restore FP or LR. This is deliberate code generation inCodeGen::genLclHeap,src/coreclr/jit/codegenarm64.cpp:3354–3362, which emits:for suitable constant-sized stack allocations.
2. Exact incorrect classification
In
src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp:1169–1177,TrailingEpilogueInstructionsCountcontains:The backward scan from the interrupted PC reaches the allocation instruction at
+0xb0. For its encoding:Therefore, the function returns
-1, despite this being an allocation in the method body.On Linux ARM64,
IsUnwindablereturns whetherTrailingEpilogueInstructionsCount(...) == 0(UnixNativeCodeManager.cpp:592–612). It consequently returns false.The matching executable’s disassembly contains this check; this is not merely a discrepancy inferred from the checkout.
3. The interrupted location is genuinely GC-interruptible
An independent decoding of the executable’s version-5 GC metadata, following the repository’s GC decoder format, produced:
Thus this is an interruptible method-body location, not a call-return safepoint accidentally interpreted as one.
UnixNativeCodeManager::IsSafePointacceptsdecoder.IsInterruptible()as well asdecoder.IsSafePoint()(UnixNativeCodeManager.cpp:399–423). The resulting combination is:That directly explains the assertion in
thread.cpp:709.4. The frame has usable unwind information
The FDE describes the established frame as:
The core contains:
The saved LR resolves to
DoubleTests.SplitPairs+136, the expected caller. The dynamic SP allocation does not invalidate this FP-based frame description.Importantly, the observed failure is the classification assertion, not an actual failed attempt by the runtime unwinder. The runtime unwinder was not executed experimentally during this investigation.
5. Introducing change
The rejecting block was added by:
Evidence beyond blame:
This establishes the source-level introduction of the bad predicate. It is not a historical binary bisection or a determination of the first failing CI run.
6. Fix considerations
The fix belongs in the epilog/unwindability classification, not in
String.Concat, the test, or removal of the assertion.Additional concerns identified during source inspection—not separate failures observed in this core:
ADD SP,SPrejection atUnixNativeCodeManager.cpp:1181–1186can also match ordinarylocalloccode:codegenarm64.cpp:3309–3325temporarily removes the outgoing-argument area using that instruction.A direction worth evaluating is to keep precise GC unwindability detection separate from additional PAC-specific hijack restrictions, while retaining necessary handling of
AUTIASP/AUTIBSP. No exact patch has been validated, and deleting all new SP checks has not been established as safe.Reproducing the debugger observations
With the executable, symbols, and core in the current directory:
Then:
These addresses and frame numbers are specific to the supplied dump. Executable text omitted from the core is supplied by the matching executable.
Validation scope: matching core/binary inspection, instruction-level predicate replay, independent GC-metadata decoding, CFI/saved-frame inspection, and source-history comparison. No source changes, product rebuild, runtime reproduction, or candidate-fix validation were performed.
Note
This investigation write-up was generated with GitHub Copilot, including an independent GPT-6 Astra challenge of the diagnosis.
MichalStrehovsky commentedon Sep 30, 2026
Cc @SwapnilGaikwad seems to caused by the PAC-RET change.
SwapnilGaikwad commentedon Oct 1, 2026
Treating SP adjustment instructions being part of epilog doesn't seem to be a good idea. These additional instruction checks helped to bail out of hijacking conservatively (but seemingly incorrectly!).
As a temporary fix we can gate checks for these instructions during hijacking, i.e., when
TrailingEpilogueInstructionsCount()is called fromGetReturnAddressHijackInfo(). It would let other places whereTrailingEpilogueInstructionsCount()called rely on the original mechanism and hijacking would be more conservative.A cleaner fix would be probably to rely on signing SP == CFA and retrieve signing SP accordingly for NativeAOT on non-windows. This will allow us to restore original
TrailingEpilogueInstructionsCount()logic and retrieving singing sp would not depend on having stack adjustment instructions in function body . Any thoughts?jkotas commentedon Oct 2, 2026
The other places are only used for debug checks. If we see this assert firing too often in CI, the easiest way to fix that is by commenting it out and accepting that the IsUnwindable check can give false negatives on arm64 like it does on other plaforms:
runtime/src/coreclr/nativeaot/Runtime/thread.cpp
Lines 701 to 704 in 32314b9
I am not sure how it would work. Can you elaborate?
Another way to address this would be to encode the prologs/epilogs in GCInfo as suggested in #101932 .
SwapnilGaikwad commentedon Oct 2, 2026
I'll take a look at #101932. I've created a draft patch that uses CFA as singing SP for NativeAOT. Also put an assert checking this in
unwindarm64.cpp. Could you take a look and see if that's reasonable, please?Arm64:[PAC-RET]Fix ARM64 epilog detection and PAC signing SP recovery
Arm64:[PAC-RET]Fix ARM64 epilog detection and PAC signing SP recovery (…