Description
See https://dev.azure.com/dnceng-public/public/_build/results?buildId=1441945&view=ms.vss-test-web.build-test-results-tab&runId=39973646&resultId=120444&paneView=debug
I hit a GCStress failure in win-x86 CoreCLR where GC stack scanning reports an interior pointer that points into a free object. The failing method is Tier0 QuickJitted code for System.Threading.Tasks.Task+WhenAllPromise..ctor(System.ReadOnlySpan<System.Threading.Tasks.Task>) .
I'm not sure whether it's a x86-specific or tier0-only issue.
Reproduction Steps
using System;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Test
{
static int returnVal = 100;
static byte[][] s = new byte[1000][];
public const int DefaultSeed = 20010415;
public static int Seed = Environment.GetEnvironmentVariable("CORECLR_SEED") switch
{
string seedStr when seedStr.Equals("random", StringComparison.OrdinalIgnoreCase) => new Random().Next(),
string seedStr when int.TryParse(seedStr, out int envSeed) => envSeed,
_ => DefaultSeed
};
static void Work()
{
for (uint i = 0; i < 1000000; i++)
{
var a = s[i++ % s.Length];
ref byte p = ref a[0];
ref byte q = ref a[1];
if (Unsafe.ByteOffset(ref p, ref q) != new IntPtr(1))
{
Console.WriteLine("ERROR: i = " + i);
returnVal = -1;
}
p = 1; q = 2;
}
}
public static int Main()
{
for(int i = 0; i < s.Length; i++) s[i] = new byte[2];
List<Task> tasks = new List<Task>();
for(int i = 0; i < 5; i++)
{
tasks.Add(Task.Run(Work));
}
Random r = new Random(Seed);
for (uint i = 0; i < 10000; i++)
{
s[r.Next(s.Length)] = new byte[3 + r.Next(100)];
}
Task t = Task.WhenAll(tasks);
t.Wait();
return returnVal;
}
}
on win-x86 run it with:
set DOTNET_TieredCompilation=1
set DOTNET_ReadyToRun=0
set DOTNET_JitDisasm=System.Threading.Tasks.Task+WhenAllPromise:*
set DOTNET_TC_QuickJitForLoops=1
set DOTNET_TieredPGO=1
set DOTNET_JitDelegateProfiling=1
set DOTNET_JitVTableProfiling=1
set DOTNET_JitDisasmWithGC=1
set DOTNET_GCStress=0xC
set DOTNET_ZapDisable=1
set DOTNET_TC_QuickJit=1
set DOTNET_TC_CallCounting=0
set DOTNET_JitGCDump=System.Threading.Tasks.Task+WhenAllPromise:*
set DOTNET_JitDisasmWithAddress=1
set DOTNET_JitDisasmWithCodeBytes=1
Then you will likely hit an AV after getting output like:
; Assembly listing for method System.Threading.Tasks.Task+WhenAllPromise:.ctor(System.ReadOnlySpan`1[System.Threading.Tasks.Task]):this (Tier0)
; Emitting BLENDED_CODE for x86 + VEX on Windows
; Tier0 code
; ebp based frame
; fully interruptible
; compiling with minopt
; Final local variable assignments
;
; V00 this [V00 ] ( 1, 1 ) ref -> [ebp-0x04] do-not-enreg[] this class-hnd <System.Threading.Tasks.Task+WhenAllPromise>
; V01 arg1 [V01 ] ( 1, 1 ) struct ( 8) [ebp+0x08] do-not-enreg[SF] ld-addr-op <System.ReadOnlySpan`1[System.Threading.Tasks.Task]>
; V02 loc0 [V02 ] ( 1, 1 ) struct ( 8) [ebp-0x0C] do-not-enreg[SF] must-init ld-addr-op <System.ReadOnlySpan`1[System.Threading.Tasks.Task]>
; V03 loc1 [V03 ] ( 1, 1 ) int -> [ebp-0x10] do-not-enreg[]
; V04 loc2 [V04 ] ( 1, 1 ) struct ( 8) [ebp-0x18] do-not-enreg[SF] must-init ld-addr-op <System.ReadOnlySpan`1[System.Threading.Tasks.Task]>
; V05 loc3 [V05 ] ( 1, 1 ) ref -> [ebp-0x1C] do-not-enreg[] must-init class-hnd <System.Threading.Tasks.Task>
; V06 tmp0 [V06 ] ( 1, 1 ) ref -> [ebp-0x20] do-not-enreg[] must-init class-hnd exact "non-inline candidate call" <System.Threading.Tasks.TplEventSource>
; V07 tmp1 [V07 ] ( 1, 1 ) int -> [ebp-0x24] do-not-enreg[] "argument with side effect"
;
; Lcl frame size = 36
G_M20174_IG01: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, prolog, nogc
0c0230b0 000000 55 push ebp
0c0230b1 000001 8BEC mov ebp, esp
0c0230b3 000003 83EC24 sub esp, 36
0c0230b6 000006 C5D857E4 vxorps xmm4, xmm4, xmm4
0c0230ba 00000A C5FA7F65E0 vmovdqu xmmword ptr [ebp-0x20], xmm4
0c0230bf 00000F 33C0 xor eax, eax
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (xor: 1) 32B boundary ...............................
0c0230c1 000011 8945F0 mov dword ptr [ebp-0x10], eax
0c0230c4 000014 8945F4 mov dword ptr [ebp-0x0C], eax
0c0230c7 000017 8945F8 mov dword ptr [ebp-0x08], eax
0c0230ca 00001A 894DFC mov gword ptr [ebp-0x04], ecx
;; size=29 bbWeight=1 PerfScore 7.08
G_M20174_IG02: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c0230cd 00001D 8B4DFC mov ecx, gword ptr [ebp-0x04]
; gcrRegs +[ecx]
0c0230d0 000020 FF157858D80B call [System.Threading.Tasks.Task:.ctor():this]
; gcrRegs -[ecx]
0c0230d6 000026 837D0C00 cmp dword ptr [ebp+0x0C], 0
0c0230da 00002A 0F95C1 setne cl
0c0230dd 00002D 0FB6C9 movzx ecx, cl
; ............................... 32B boundary ...............................
0c0230e0 000030 BA74DEA308 mov edx, 0x8A3DE74 ; 'Expected a non-zero length task array'
; gcrRegs +[edx]
0c0230e5 000035 FF15A0756F05 call [System.Diagnostics.Debug:Assert(bool,System.String)]
; gcrRegs -[edx]
0c0230eb 00003B 8B45FC mov eax, gword ptr [ebp-0x04]
; gcrRegs +[eax]
0c0230ee 00003E 83780800 cmp gword ptr [eax+0x08], 0
0c0230f2 000042 0F94C1 sete cl
0c0230f5 000045 0FB6C9 movzx ecx, cl
0c0230f8 000048 BACCDEA308 mov edx, 0x8A3DECC ; 'Expected to be able to use the state object field for faulted/c'
; gcrRegs +[edx]
0c0230fd 00004D FF15A0756F05 call [System.Diagnostics.Debug:Assert(bool,System.String)]
; gcrRegs -[eax edx]
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (call: 3 ; jcc erratum) 32B boundary ...............................
0c023103 000053 8B45FC mov eax, gword ptr [ebp-0x04]
; gcrRegs +[eax]
0c023106 000056 81481C00080000 or dword ptr [eax+0x1C], 0x800
0c02310d 00005D 8B4508 mov eax, dword ptr [ebp+0x08]
; gcrRegs -[eax]
0c023110 000060 8945F4 mov dword ptr [ebp-0x0C], eax
0c023113 000063 8B450C mov eax, dword ptr [ebp+0x0C]
0c023116 000066 8945F8 mov dword ptr [ebp-0x08], eax
0c023119 000069 33C0 xor eax, eax
0c02311b 00006B 8945F0 mov dword ptr [ebp-0x10], eax
0c02311e 00006E EB2F jmp SHORT G_M20174_IG05
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (jmp: 0 ; jcc erratum) 32B boundary ...............................
;; size=83 bbWeight=1 PerfScore 27.25
G_M20174_IG03: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c023120 000070 8B45F8 mov eax, dword ptr [ebp-0x08]
0c023123 000073 3945F0 cmp dword ptr [ebp-0x10], eax
0c023126 000076 0F83F4000000 jae G_M20174_IG14
0c02312c 00007C 8B45F4 mov eax, bword ptr [ebp-0x0C]
; byrRegs +[eax]
0c02312f 00007F 8B55F0 mov edx, dword ptr [ebp-0x10]
0c023132 000082 833C9000 cmp gword ptr [eax+4*edx], 0
0c023136 000086 7510 jne SHORT G_M20174_IG04
0c023138 000088 B91D000000 mov ecx, 29
0c02313d 00008D BA33000000 mov edx, 51
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (mov: 2) 32B boundary ...............................
0c023142 000092 FF1528517109 call [System.ThrowHelper:ThrowArgumentException(int,int)]
; byrRegs -[eax]
;; size=40 bbWeight=1 PerfScore 11.50
G_M20174_IG04: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref
0c023148 000098 8B45F0 mov eax, dword ptr [ebp-0x10]
0c02314b 00009B 40 inc eax
0c02314c 00009C 8945F0 mov dword ptr [ebp-0x10], eax
;; size=7 bbWeight=1 PerfScore 2.25
G_M20174_IG05: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c02314f 00009F 8B45F0 mov eax, dword ptr [ebp-0x10]
0c023152 0000A2 3B45F8 cmp eax, dword ptr [ebp-0x08]
0c023155 0000A5 7CC9 jl SHORT G_M20174_IG03
0c023157 0000A7 8B0DC8168D07 mov ecx, gword ptr [0x078D16C8] ; static handle
; gcrRegs +[ecx]
0c02315d 0000AD 3909 cmp dword ptr [ecx], ecx
0c02315f 0000AF FF15A8F0BA09 call [System.Diagnostics.Tracing.EventSource:IsEnabled():bool:this]
; gcrRegs -[ecx]
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (call: 5 ; jcc erratum) 32B boundary ...............................
0c023165 0000B5 85C0 test eax, eax
0c023167 0000B7 742B je SHORT G_M20174_IG06
0c023169 0000B9 A1C8168D07 mov eax, gword ptr [0x078D16C8] ; static handle
; gcrRegs +[eax]
0c02316e 0000BE 8945E0 mov gword ptr [ebp-0x20], eax
0c023171 0000C1 8B4DFC mov ecx, gword ptr [ebp-0x04]
; gcrRegs +[ecx]
0c023174 0000C4 FF15D8F0BA09 call [System.Threading.Tasks.Task:get_Id():int:this]
; gcrRegs -[eax ecx]
0c02317a 0000CA 8945DC mov dword ptr [ebp-0x24], eax
0c02317d 0000CD 6874DFA308 push 0x8A3DF74
; gcr arg push 0
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (push: 2) 32B boundary ...............................
0c023182 0000D2 6A00 push 0
; npt arg push 1
0c023184 0000D4 6A00 push 0
; npt arg push 2
0c023186 0000D6 8B55DC mov edx, dword ptr [ebp-0x24]
0c023189 0000D9 8B4DE0 mov ecx, gword ptr [ebp-0x20]
; gcrRegs +[ecx]
0c02318c 0000DC 3909 cmp dword ptr [ecx], ecx
0c02318e 0000DE FF1508F1BA09 call [System.Threading.Tasks.TplEventSource:TraceOperationBegin(int,System.String,long):this]
; gcrRegs -[ecx]
; gcr arg pop 3
;; size=69 bbWeight=1 PerfScore 29.25
G_M20174_IG06: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c023194 0000E4 0FB60588B29303 movzx eax, byte ptr [0x0393B288] ; static handle
0c02319b 0000EB 85C0 test eax, eax
0c02319d 0000ED 7409 je SHORT G_M20174_IG07
0c02319f 0000EF 8B4DFC mov ecx, gword ptr [ebp-0x04]
; gcrRegs +[ecx]
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (mov: 2) 32B boundary ...............................
0c0231a2 0000F2 FF1590F0BA09 call [System.Threading.Tasks.Task:AddToActiveTasks(System.Threading.Tasks.Task):bool]
; gcrRegs -[ecx]
;; size=20 bbWeight=1 PerfScore 7.25
G_M20174_IG07: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c0231a8 0000F8 8B45FC mov eax, gword ptr [ebp-0x04]
; gcrRegs +[eax]
0c0231ab 0000FB 8B550C mov edx, dword ptr [ebp+0x0C]
0c0231ae 0000FE 895020 mov dword ptr [eax+0x20], edx
0c0231b1 000101 8B4508 mov eax, dword ptr [ebp+0x08]
; gcrRegs -[eax]
0c0231b4 000104 8945E8 mov dword ptr [ebp-0x18], eax
0c0231b7 000107 8B450C mov eax, dword ptr [ebp+0x0C]
0c0231ba 00010A 8945EC mov dword ptr [ebp-0x14], eax
0c0231bd 00010D 33C0 xor eax, eax
0c0231bf 00010F 8945F0 mov dword ptr [ebp-0x10], eax
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (mov: 2) 32B boundary ...............................
0c0231c2 000112 EB4E jmp SHORT G_M20174_IG12
;; size=28 bbWeight=1 PerfScore 10.25
G_M20174_IG08: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c0231c4 000114 8B45EC mov eax, dword ptr [ebp-0x14]
0c0231c7 000117 3945F0 cmp dword ptr [ebp-0x10], eax
0c0231ca 00011A 7354 jae SHORT G_M20174_IG14
0c0231cc 00011C 8B45E8 mov eax, bword ptr [ebp-0x18]
; byrRegs +[eax]
0c0231cf 00011F 8B55F0 mov edx, dword ptr [ebp-0x10]
0c0231d2 000122 8B0490 mov eax, gword ptr [eax+4*edx]
; gcrRegs +[eax]
; byrRegs -[eax]
0c0231d5 000125 8945E4 mov gword ptr [ebp-0x1C], eax
0c0231d8 000128 837DE400 cmp gword ptr [ebp-0x1C], 0
0c0231dc 00012C 740F je SHORT G_M20174_IG09
0c0231de 00012E 8B4DE4 mov ecx, gword ptr [ebp-0x1C]
; gcrRegs +[ecx]
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (mov: 1) 32B boundary ...............................
0c0231e1 000131 3909 cmp dword ptr [ecx], ecx
0c0231e3 000133 FF15B07DCF0B call [System.Threading.Tasks.Task:get_IsCompleted():bool:this]
; gcrRegs -[eax ecx]
0c0231e9 000139 85C0 test eax, eax
0c0231eb 00013B 740E je SHORT G_M20174_IG10
;; size=41 bbWeight=1 PerfScore 17.25
G_M20174_IG09: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c0231ed 00013D 8B4DFC mov ecx, gword ptr [ebp-0x04]
; gcrRegs +[ecx]
0c0231f0 000140 8B55E4 mov edx, gword ptr [ebp-0x1C]
; gcrRegs +[edx]
0c0231f3 000143 FF15CC05040C call [System.Threading.Tasks.Task+WhenAllPromise:Invoke(System.Threading.Tasks.Task):this]
; gcrRegs -[ecx edx]
0c0231f9 000149 EB10 jmp SHORT G_M20174_IG11
;; size=14 bbWeight=1 PerfScore 7.00
G_M20174_IG10: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref
0c0231fb 00014B 6A00 push 0
; npt arg push 0
0c0231fd 00014D 8B4DE4 mov ecx, gword ptr [ebp-0x1C]
; gcrRegs +[ecx]
; ............................... 32B boundary ...............................
0c023200 000150 8B55FC mov edx, gword ptr [ebp-0x04]
; gcrRegs +[edx]
0c023203 000153 3909 cmp dword ptr [ecx], ecx
0c023205 000155 FF15C058D80B call [System.Threading.Tasks.Task:AddCompletionAction(System.Threading.Tasks.ITaskCompletionAction,bool):this]
; gcrRegs -[ecx edx]
; gcr arg pop 1
;; size=16 bbWeight=1 PerfScore 8.00
G_M20174_IG11: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref
0c02320b 00015B 8B45F0 mov eax, dword ptr [ebp-0x10]
0c02320e 00015E 40 inc eax
0c02320f 00015F 8945F0 mov dword ptr [ebp-0x10], eax
;; size=7 bbWeight=1 PerfScore 2.25
G_M20174_IG12: ; bbWeight=1, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byref, isz
0c023212 000162 8B45F0 mov eax, dword ptr [ebp-0x10]
0c023215 000165 3B45EC cmp eax, dword ptr [ebp-0x14]
0c023218 000168 7CAA jl SHORT G_M20174_IG08
;; size=8 bbWeight=1 PerfScore 3.00
G_M20174_IG13: ; bbWeight=1, epilog, nogc, extend
0c02321a 00016A 8BE5 mov esp, ebp
0c02321c 00016C 5D pop ebp
0c02321d 00016D C20800 ret 8
; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ (ret: 0 ; jcc erratum) 32B boundary ...............................
;; size=6 bbWeight=1 PerfScore 2.75
G_M20174_IG14: ; bbWeight=0, gcVars=00000000 {}, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, gcvars, byref
0c023220 000170 E8AB006DF9 call CORINFO_HELP_RNGCHKFAIL
0c023225 000175 CC int3
;; size=6 bbWeight=0 PerfScore 0.00
; Total bytes of code 374, prolog size 29, PerfScore 135.08, instruction count 114, allocated bytes for code 374 (MethodHash=eb9ab131) for method System.Threading.Tasks.Task+WhenAllPromise:.ctor(System.ReadOnlySpan`1[System.Threading.Tasks.Task]):this (Tier0)
; ============================================================
GC Info for method System.Threading.Tasks.Task+WhenAllPromise:.ctor(System.ReadOnlySpan`1[System.Threading.Tasks.Task]):this
GC info size = 86
Method info block:
method size = 0176
prolog size = 29
epilog size = 6
epilog count = 1
epilog end = no
callee-saved regs = EBP
ebp frame = yes
fully interruptible= yes
double align = no
arguments size = 2 DWORDs
stack frame size = 9 DWORDs
untracked count = 6
var ptr tab count = 0
epilog # 0 at 016A
argTabOffset = 6
82 76 80 8A D9 |
C7 A8 94 F5 BE |
BF 40 06 82 6A |
06 4D 14 ...|
Pointer table:
04 14 0C ...| [EBP-04H] an untracked local
4D 0C 05 ...| [EBP+08H] an untracked byref local
14 05 04 ...| [EBP-0CH] an untracked byref local
0C 04 F3 ...| [EBP-18H] an untracked byref local
05 F3 48 ...| [EBP-1CH] an untracked local
04 48 0E ...| [EBP-20H] an untracked local
F3 48 F0 ...| 0020 reg ECX becoming live
0E 57 16 ...| 0026 reg ECX becoming dead
F0 57 43 ...| 0035 reg EDX becoming live
16 F0 57 ...| 003B reg EDX becoming dead
43 57 06 ...| 003E reg EAX becoming live
F0 57 10 ...| 004D reg EDX becoming live
06 43 F0 ...| 0053 reg EAX becoming dead
10 F0 02 ...| 0053 reg EDX becoming dead
43 02 F2 ...| 0056 reg EAX becoming live
F0 02 BF ...| 0060 reg EAX becoming dead
F2 BF 47 ...| 007F reg EAX becoming live (iptr)
F2 01 4D ...| 0098 reg EAX becoming dead
F1 4D 08 ...| 00AD reg ECX becoming live
F0 08 41 ...| 00B5 reg ECX becoming dead
F0 41 06 ...| 00BE reg EAX becoming live
4E 08 F0 ...| 00C4 reg ECX becoming live
06 F0 80 ...| 00CA reg EAX becoming dead
08 80 B2 ...| 00CA reg ECX becoming dead
F0 80 B2 ...| 00D2 push ptr 0
B2 4E F0 ...| 00D4 push non-ptr (1)
B2 F0 08 ...| 00D6 push non-ptr (2)
4E 08 D8 ...| 00DC reg ECX becoming live
F0 08 F0 ...| 00E4 reg ECX becoming dead
D8 4E 0E ...| 00E4 pop 3 ptrs
F0 4E 43 ...| 00F2 reg ECX becoming live
0E F0 01 ...| 00F8 reg ECX becoming dead
43 01 F2 ...| 00FB reg EAX becoming live
Expected behavior
No crash.
Actual behavior
Crashed with AV.
Stacktrace:
[0x0] coreclr!FailFastOnAssert+0x22() [D:\a\_work\1\s\src\coreclr\utilcode\debug.cpp @ 68] 0x9bcb524 0x72c56caf
[0x1] coreclr!_DbgBreakCheck+0x52f(char * szFile = 0x7362cdcc : "D:\a\_work\1\s\src\coreclr\vm\methodtable.cpp", int iLine = 8659, char * szExpr = 0x73786a08 : "SanityCheck()..CORECLR! Object::ValidateInner + 0x137 (0x72fc0747).CORECLR! Object::Validate + 0xA9 (0x72fc05b9).CORECLR! WKS::GCHeap::Promote + 0x94 (0x7342ca44).CORECLR! PromoteCarefully + 0x81 (0x730165f1).CORECLR! GcEnumObject + 0x143 (0x73164383).CORECLR! EnumGcRefsX86 + 0xD19 (0x72e824e9).CORECLR! EECodeManager::EnumGcRefs + 0x1D2 (0x72e81772).CORECLR! GcStackCrawlCallBack + 0x2B9 (0x73164849).CORECLR! Thread::StackWalkFramesEx + 0x223 (0x7301b633).CORECLR! Thread::StackWalkFrames + 0x13B (0x7301b37b", int fConstrained = 0) [D:\a\_work\1\s\src\coreclr\utilcode\debug.cpp @ 265] 0x9bcb534 0x72c55ff9
[0x2] coreclr!_DbgBreakCheckNoThrow+0x79(char * szFile = 0x7362cdcc : "D:\a\_work\1\s\src\coreclr\vm\methodtable.cpp", int iLine = 8659, char * szExpr = 0x73786a08 : "SanityCheck()..CORECLR! Object::ValidateInner + 0x137 (0x72fc0747).CORECLR! Object::Validate + 0xA9 (0x72fc05b9).CORECLR! WKS::GCHeap::Promote + 0x94 (0x7342ca44).CORECLR! PromoteCarefully + 0x81 (0x730165f1).CORECLR! GcEnumObject + 0x143 (0x73164383).CORECLR! EnumGcRefsX86 + 0xD19 (0x72e824e9).CORECLR! EECodeManager::EnumGcRefs + 0x1D2 (0x72e81772).CORECLR! GcStackCrawlCallBack + 0x2B9 (0x73164849).CORECLR! Thread::StackWalkFramesEx + 0x223 (0x7301b633).CORECLR! Thread::StackWalkFrames + 0x13B (0x7301b37b", int fConstrained = 0) [D:\a\_work\1\s\src\coreclr\utilcode\debug.cpp @ 320] 0x9bcc604 0x72c5641c
[0x3] coreclr!DbgAssertDialog+0x27c(char * szFile = 0x7362cdcc : "D:\a\_work\1\s\src\coreclr\vm\methodtable.cpp", int iLine = 8659, char * szExpr = 0x7362fae4 : "SanityCheck()") [D:\a\_work\1\s\src\coreclr\utilcode\debug.cpp @ 439] 0x9bcc674 0x72fb0c01
[0x4] coreclr!MethodTable::Validate+0x31() [D:\a\_work\1\s\src\coreclr\vm\methodtable.cpp @ 8659] 0x9bcc718 0x72fc0747
[0x5] coreclr!Object::ValidateInner+0x137(int bDeep = 1, int bVerifyNextHeader = 1, int bVerifySyncBlock = 1) [D:\a\_work\1\s\src\coreclr\vm\object.cpp @ 548] 0x9bcc73c 0x72fc05b9
[0x6] coreclr!Object::Validate+0xa9(int bDeep = 1, int bVerifyNextHeader = 1, int bVerifySyncBlock = 1) [D:\a\_work\1\s\src\coreclr\vm\object.cpp @ 525] 0x9bcc7c8 0x7342ca44
[0x7] coreclr!WKS::GCHeap::Promote+0x94(Object * * ppObject = 0x356edc8, ScanContext * sc = 0x9bcdd2c, unsigned int flags = 0x1) [D:\a\_work\1\s\src\coreclr\gc\interface.cpp @ 1113] 0x9bcc7fc 0x730165f1
[0x8] coreclr!PromoteCarefully+0x81(void (*)(Object * *,ScanContext *,unsigned int) fn = 0x7342c9b0 : coreclr!WKS::GCHeap::Promote+0x0, Object * * ppObj = 0x356edc8, ScanContext * sc = 0x9bcdd2c, unsigned int flags = 0x1) [D:\a\_work\1\s\src\coreclr\vm\siginfo.cpp @ 5218] 0x9bcc820 0x73164383
[0x9] coreclr!GcEnumObject+0x143(void * pData = 0x9bcdbb0, OBJECTREF * pObj = 0x356edc8, unsigned int flags = 0x1) [D:\a\_work\1\s\src\coreclr\vm\gcenv.ee.common.cpp @ 219] 0x9bcc840 0x72e824e9
[0xa] coreclr!EnumGcRefsX86+0xd19(REGDISPLAY * pContext = 0x9bcd2a8, unsigned char * methodStart = 0x9f7cfd0 : 0x55, unsigned long curOffs = 0x98, GCInfoToken gcInfoToken, unsigned char * funcletStart = 0x97adbb0 : 0x55, bool isFunclet = false, bool isFilterFunclet = false, unsigned int flags = 0x0, void (*)(void *,OBJECTREF *,unsigned int) pCallBack = 0x73164240 : coreclr!GcEnumObject+0x0, void * hCallBack = 0x9bcdbb0) [D:\a\_work\1\s\src\coreclr\vm\gc_unwind_x86.inl @ 3516] 0x9bcc878 0x72e81772
[0xb] coreclr!EECodeManager::EnumGcRefs+0x1d2(REGDISPLAY * pContext = 0x9bcd2a8, EECodeInfo * pCodeInfo = 0x9bcceb8, unsigned int flags = 0x0, void (*)(void *,OBJECTREF *,unsigned int) pCallBack = 0x73164240 : coreclr!GcEnumObject+0x0, void * hCallBack = 0x9bcdbb0, unsigned long relOffsetOverride = 0xffffffff) [D:\a\_work\1\s\src\coreclr\vm\eetwain.cpp @ 1077] 0x9bccbd4 0x73164849
[0xc] coreclr!GcStackCrawlCallBack+0x2b9(CrawlFrame * pCF = 0x9bccea0, void * pData = 0x9bcdbb0) [D:\a\_work\1\s\src\coreclr\vm\gcenv.ee.common.cpp @ 325] 0x9bccc98 0x7301b633
[0xd] coreclr!Thread::MakeStackwalkerCallback+0x73() [D:\a\_work\1\s\src\coreclr\vm\stackwalk.cpp @ 763] (Inline Function) (Inline Function)
[0xe] coreclr!Thread::StackWalkFramesEx+0x223(REGDISPLAY * pRD = 0x9bcd2a8, StackWalkAction (*)(CrawlFrame *,void *) pCallback = 0x73164590 : coreclr!GcStackCrawlCallBack+0x0, void * pData = 0x9bcdbb0, unsigned int flags = 0x8500, Frame * pStartFrame = 0x0) [D:\a\_work\1\s\src\coreclr\vm\stackwalk.cpp @ 826] 0x9bcce78 0x7301b37b
[0xf] coreclr!Thread::StackWalkFrames+0x13b(StackWalkAction (*)(CrawlFrame *,void *) pCallback = 0x73164590 : coreclr!GcStackCrawlCallBack+0x0, void * pData = 0x9bcdbb0, unsigned int flags = 0x8500, Frame * pStartFrame = 0x0) [D:\a\_work\1\s\src\coreclr\vm\stackwalk.cpp @ 901] 0x9bcd27c 0x73161a07
[0x10] coreclr!ScanStackRoots+0x307(Thread * pThread = 0x37bd8c8, void (*)(Object * *,ScanContext *,unsigned int) fn = 0x7342c9b0 : coreclr!WKS::GCHeap::Promote+0x0, ScanContext * sc = 0x9bcdd2c) [D:\a\_work\1\s\src\coreclr\vm\gcenv.ee.cpp @ 199] 0x9bcdb7c 0x7315ef6e
[0x11] coreclr!GCToEEInterface::GcScanRoots+0x16e(void (*)(Object * *,ScanContext *,unsigned int) fn = 0x7342c9b0 : coreclr!WKS::GCHeap::Promote+0x0, int condemned = 2, int max_gen = 2, ScanContext * sc = 0x9bcdd2c) [D:\a\_work\1\s\src\coreclr\vm\gcenv.ee.cpp @ 314] 0x9bcdca4 0x73445df1
[0x12] coreclr!WKS::gc_heap::mark_phase+0x231(int condemned_gen_number = 2) [D:\a\_work\1\s\src\coreclr\gc\mark_phase.cpp @ 3163] 0x9bcdcc8 0x7343e416
[0x13] coreclr!WKS::gc_heap::gc1+0x1b6() [D:\a\_work\1\s\src\coreclr\gc\collect.cpp @ 160] 0x9bcdd5c 0x7343e225
[0x14] coreclr!WKS::gc_heap::garbage_collect+0x815(int n = 2) [D:\a\_work\1\s\src\coreclr\gc\collect.cpp @ 1300] 0x9bcddc4 0x7342b1f4
[0x15] coreclr!WKS::GCHeap::GarbageCollectGeneration+0x3e4(unsigned int gen = 0x2, gc_reason reason = reason_gcstress (8)) [D:\a\_work\1\s\src\coreclr\gc\interface.cpp @ 1932] 0x9bcdde8 0x7342b3de
[0x16] coreclr!WKS::GCHeap::GarbageCollectTry+0x9e(int generation = 2, int low_memory_p = 0, int mode = -2147483648) [D:\a\_work\1\s\src\coreclr\gc\interface.cpp @ 1818] 0x9bcde18 0x7342db9e
[0x17] coreclr!WKS::GCHeap::StressHeap+0x44e(gc_alloc_context * context = 0x92c2158) [D:\a\_work\1\s\src\coreclr\gc\interface.cpp @ 1397] 0x9bcde2c 0x7315b847
[0x18] coreclr!DoGcStress+0x727(_CONTEXT * regs = 0x9bced0c, NativeCodeVersion nativeCodeVersion) [D:\a\_work\1\s\src\coreclr\vm\gccover.cpp @ 901] 0x9bcde74 0x7315bc93
[0x19] coreclr!OnGcCoverageInterrupt+0x213(_CONTEXT * regs = 0x9bced0c) [D:\a\_work\1\s\src\coreclr\vm\gccover.cpp @ 1381] 0x9bce96c 0x72e93989
[0x1a] coreclr!IsGcMarker+0x9a(_CONTEXT * pContext = 0x9bced0c, pExceptionRecord = <unavailable>) [D:\a\_work\1\s\src\coreclr\vm\excep.cpp @ 5174] (Inline Function) (Inline Function)
[0x1b] coreclr!CLRVectoredExceptionHandlerShim+0x159(_EXCEPTION_POINTERS * pExceptionInfo = 0x9bcebdc) [D:\a\_work\1\s\src\coreclr\vm\excep.cpp @ 6461] 0x9bceb50 0x76fdf441
[0x1c] ntdll!RtlpCallVectoredHandlers+0x39460() 0x9bcebcc 0x76f9ec6a
[0x1d] ntdll!RtlDispatchException+0x7c() 0x9bcec14 0x76fb134f
[0x1e] ntdll!KiUserExceptionDispatcher+0xf 0x9bcecac 0xa173407
[0x1f] System_Private_CoreLib!System.Threading.WaitHandle.WaitForMultipleObjectsIgnoringSyncContext(IntPtr*, Int32, Boolean, Int32, Boolean)+0x77 [/_/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @ 31] 0x9bcf7ac 0xa173274
[0x20] System_Private_CoreLib!System.Threading.WaitHandle.WaitOneCore(IntPtr, Int32, Boolean)+0x24 [/_/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.cs @ 130] 0x9bcf868 0xa172449
[0x21] System_Private_CoreLib!System.Threading.WaitHandle.WaitOneNoCheck(Int32, Boolean, System.Object, WaitHandleWaitSourceMap)+0x1f1 [/_/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @ 181] 0x9bcf87c 0xa17220b
[0x22] System_Private_CoreLib!System.Threading.WaitHandle.WaitOne()+0x1b [/_/src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.cs @ 498] 0x9bcf8e0 0x97a9c42
[0x23] System_Private_CoreLib!System.Threading.PortableThreadPool+GateThread.GateThreadStart()+0x142 [/_/src/libraries/System.Private.CoreLib/src/System/Threading/PortableThreadPool.GateThread.cs @ 65] 0x9bcf8ec 0x97a915b
[0x24] System_Private_CoreLib!System.Threading.Thread.StartCallback(System.Threading.Thread*)+0x93 [/_/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 109] 0x9bcf994 0x730d882e
[0x25] coreclr!UnmanagedCallersOnlyCaller::InvokeDirect<OBJECTREF *>+0x18e(OBJECTREF * <args_0> = 0x9bcfb94) [D:\a\_work\1\s\src\coreclr\vm\callhelpers.h @ 675] 0x9bcf9c0 0x730d9219
[0x26] coreclr!KickOffThread_Worker+0x229(void * ptr = 0x0) [D:\a\_work\1\s\src\coreclr\vm\comsynchronizable.cpp @ 146] 0x9bcfac4 0x7303818d
[0x27] coreclr!ManagedThreadBase_DispatchInner+0xcd(ManagedThreadCallState * pCallState = 0x9bcfe40) [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 5733] 0x9bcfbc4 0x7303829e
[0x28] coreclr!ManagedThreadBase_DispatchMiddle+0xbe(ManagedThreadCallState * pCallState = 0x9bcfe40) [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 5777] 0x9bcfc38 0x7303a43d
[0x29] coreclr!``ManagedThreadBase_DispatchOuter'::`8'::__Body::Run'::`5'::__Body::Run+0x8d(Param * pParam = 0x9bcfdac) [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 5930] 0x9bcfd00 0x7303a55e
[0x2a] coreclr!`ManagedThreadBase_DispatchOuter'::`8'::__Body::Run+0x9e(ManagedThreadBase_DispatchOuter::__l2::TryArgs * pArgs = 0x9bcfd9c) [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 5930] 0x9bcfd40 0x730386fb
[0x2b] coreclr!ManagedThreadBase_DispatchOuter+0xdb(ManagedThreadCallState * pCallState = 0x9bcfe40) [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 5948] 0x9bcfd88 0x73037c8f
[0x2c] coreclr!ManagedThreadBase::KickOff+0xcf(void (*)(void *) pTarget = 0x730d8ff0 : coreclr!KickOffThread_Worker+0x0, void * args = 0x0) [D:\a\_work\1\s\src\coreclr\vm\threads.cpp @ 5967] 0x9bcfde8 0x730d8f75
[0x2d] coreclr!KickOffThread+0x1c5(void * pass = 0x92c2380) [D:\a\_work\1\s\src\coreclr\vm\comsynchronizable.cpp @ 189] 0x9bcfe60 0x73b962c4
[0x2e] kernel32!BaseThreadInitThunk+0x24 0x9bcff24 0x76fa0aa9
[0x2f] ntdll!__RtlUserThreadStart+0x2f() 0x9bcff38 0x76fa0a74
[0x30] ntdll!_RtlUserThreadStart+0x1b 0x9bcff80 0x0
Regression?
Yes
Known Workarounds
No response
Configuration
Can reproduce with the main branch.
Other information
Analysis from copilot (Click to expand the details)
Analysis
The failure appears to be caused by bad GC info during a copy of the byref field of a ReadOnlySpan<Task> from an incoming stack argument to a local stack home.
The generated code copies the span byref like this:
00005D 8B4508 mov eax, dword ptr [ebp+0x08]
; gcrRegs -[eax]
000060 8945F4 mov dword ptr [ebp-0x0C], eax
At native offset 0x5D, eax is loaded with the byref/interior pointer field from the incoming ReadOnlySpan<Task> argument. However, the GC info does not appear to mark eax as a byref/interior pointer during the interval before the store at offset 0x60.
Under GCStress, a GC can occur after the load into eax and before the store to [ebp-0x0C]. If the object backing the span moves during that GC, the stack argument [ebp+0x08] is updated, but eax is not updated because it was not reported as a byref. Execution then resumes and stores the stale byref into [ebp-0x0C].
Later, [ebp-0x0C] is correctly reported as an untracked byref local, but by then it contains a stale pointer into the old object location, which is now a free object.
Failures
The GC root being promoted was:
The stack frame for the managed method had:
EBP = 0356edd4
ppObject = 0356edc8 = EBP - 0x0C
So the reported root is the byref field of the local ReadOnlySpan<Task> stored at [ebp-0x0C].
At the time of failure:
PromoteCarefully interpreted this as an interior pointer and found the containing object:
05b7359c Free Object
Size: 372 (0x174) bytes
Local variable assignments:
V00 this ref -> [ebp-0x04]
V01 arg1 ReadOnlySpan<Task> -> [ebp+0x08]
V02 loc0 ReadOnlySpan<Task> -> [ebp-0x0C]
V03 loc1 int -> [ebp-0x10]
V04 loc2 ReadOnlySpan<Task> -> [ebp-0x18]
V05 loc3 Task ref -> [ebp-0x1C]
V06 tmp0 TplEventSource ref -> [ebp-0x20]
V07 tmp1 int -> [ebp-0x24]
The first span copy:
00005D 8B4508 mov eax, dword ptr [ebp+0x08]
; gcrRegs -[eax]
000060 8945F4 mov dword ptr [ebp-0x0C], eax
000063 8B450C mov eax, dword ptr [ebp+0x0C]
000066 8945F8 mov dword ptr [ebp-0x08], eax
000069 33C0 xor eax, eax
00006B 8945F0 mov dword ptr [ebp-0x10], eax
The local span is later used here:
00007C 8B45F4 mov eax, bword ptr [ebp-0x0C]
; byrRegs +[eax]
00007F 8B55F0 mov edx, dword ptr [ebp-0x10]
000082 833C9000 cmp gword ptr [eax+4*edx], 0
000086 7510 jne SHORT G_M20174_IG04
The crash occurred at a GCStress interrupt around:
G_M20174_IG04:
000098 8B45F0 mov eax, dword ptr [ebp-0x10]
00009B 40 inc eax
00009C 8945F0 mov dword ptr [ebp-0x10], eax
At that point [ebp-0x0C] is correctly reported as an untracked byref local, but it already contains the stale byref value.
The second span copy has the same shape:
000101 8B4508 mov eax, dword ptr [ebp+0x08]
; gcrRegs -[eax]
000104 8945E8 mov dword ptr [ebp-0x18], eax
GC info
The GC info marks the stack homes as untracked byrefs:
[EBP+08H] an untracked byref local
[EBP-0CH] an untracked byref local
[EBP-18H] an untracked byref local
Relevant register transitions:
0056 reg EAX becoming live
0060 reg EAX becoming dead
007F reg EAX becoming live (iptr)
0098 reg EAX becoming dead
The 007F transition corresponds to the later load from [ebp-0x0C]:
00007C mov eax, bword ptr [ebp-0x0C]
However, the earlier load from [ebp+0x08] at 00005D appears not to make eax live as an interior pointer before the store at 000060.
Expected behavior
At the instruction interval:
00005D mov eax, dword ptr [ebp+0x08]
000060 mov dword ptr [ebp-0x0C], eax
eax contains the byref/interior pointer field of ReadOnlySpan<Task>.
Since the method is fully interruptible and GCStress can interrupt between these two instructions, eax should either:
- be reported as an interior pointer for this interval, or
- the interval should not be GC-interruptible.
Otherwise, a moving GC can update [ebp+0x08] while leaving eax stale, and the stale value is then stored into [ebp-0x0C].
Actual behavior
eax does not appear to be reported as a byref/interior pointer between the load from [ebp+0x08] and the store to [ebp-0x0C].
Under GCStress, this can leave the local span copy with a stale interior pointer:
[ebp+0x08] = updated byref
[ebp-0x0C] = stale byref into old object location
A later GC reports [ebp-0x0C] as an interior pointer. The pointer maps into a free object, and object validation asserts.
Suspected root cause
This looks like bad x86 Tier0 fully-interruptible GC info for a byref register during a stack-to-stack copy of a struct field containing a byref, specifically ReadOnlySpan<T>._reference.
The stack homes themselves are reported as untracked byrefs, which seems correct. The missing piece appears to be the temporary register used during the copy:
mov eax, [ebp+0x08] ; eax should become byref/iptr
mov [ebp-0x0C], eax
The same pattern also occurs for the second local span copy:
mov eax, [ebp+0x08]
mov [ebp-0x18], eax
Description
See https://dev.azure.com/dnceng-public/public/_build/results?buildId=1441945&view=ms.vss-test-web.build-test-results-tab&runId=39973646&resultId=120444&paneView=debug
I hit a GCStress failure in win-x86 CoreCLR where GC stack scanning reports an interior pointer that points into a free object. The failing method is Tier0 QuickJitted code for
System.Threading.Tasks.Task+WhenAllPromise..ctor(System.ReadOnlySpan<System.Threading.Tasks.Task>).I'm not sure whether it's a x86-specific or tier0-only issue.
Reproduction Steps
on win-x86 run it with:
Then you will likely hit an AV after getting output like:
Expected behavior
No crash.
Actual behavior
Crashed with AV.
Stacktrace:
Regression?
Yes
Known Workarounds
No response
Configuration
Can reproduce with the main branch.
Other information
Analysis from copilot (Click to expand the details)
Analysis
The failure appears to be caused by bad GC info during a copy of the byref field of a
ReadOnlySpan<Task>from an incoming stack argument to a local stack home.The generated code copies the span byref like this:
At native offset
0x5D,eaxis loaded with the byref/interior pointer field from the incomingReadOnlySpan<Task>argument. However, the GC info does not appear to markeaxas a byref/interior pointer during the interval before the store at offset0x60.Under GCStress, a GC can occur after the load into
eaxand before the store to[ebp-0x0C]. If the object backing the span moves during that GC, the stack argument[ebp+0x08]is updated, buteaxis not updated because it was not reported as a byref. Execution then resumes and stores the stale byref into[ebp-0x0C].Later,
[ebp-0x0C]is correctly reported as an untracked byref local, but by then it contains a stale pointer into the old object location, which is now a free object.Failures
The GC root being promoted was:
The stack frame for the managed method had:
So the reported root is the byref field of the local
ReadOnlySpan<Task>stored at[ebp-0x0C].At the time of failure:
PromoteCarefullyinterpreted this as an interior pointer and found the containing object:Local variable assignments:
The first span copy:
The local span is later used here:
The crash occurred at a GCStress interrupt around:
At that point
[ebp-0x0C]is correctly reported as an untracked byref local, but it already contains the stale byref value.The second span copy has the same shape:
GC info
The GC info marks the stack homes as untracked byrefs:
Relevant register transitions:
The
007Ftransition corresponds to the later load from[ebp-0x0C]:However, the earlier load from
[ebp+0x08]at00005Dappears not to makeeaxlive as an interior pointer before the store at000060.Expected behavior
At the instruction interval:
eaxcontains the byref/interior pointer field ofReadOnlySpan<Task>.Since the method is fully interruptible and GCStress can interrupt between these two instructions,
eaxshould either:Otherwise, a moving GC can update
[ebp+0x08]while leavingeaxstale, and the stale value is then stored into[ebp-0x0C].Actual behavior
eaxdoes not appear to be reported as a byref/interior pointer between the load from[ebp+0x08]and the store to[ebp-0x0C].Under GCStress, this can leave the local span copy with a stale interior pointer:
A later GC reports
[ebp-0x0C]as an interior pointer. The pointer maps into a free object, and object validation asserts.Suspected root cause
This looks like bad x86 Tier0 fully-interruptible GC info for a byref register during a stack-to-stack copy of a struct field containing a byref, specifically
ReadOnlySpan<T>._reference.The stack homes themselves are reported as untracked byrefs, which seems correct. The missing piece appears to be the temporary register used during the copy:
The same pattern also occurs for the second local span copy: