-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
We have noticed crashes in our CI when running tests with our profiler. We've seen crashes with .NET 6 on x86 and ARM64, but other runtime versions or platforms could also be impacted. The process crashes with an AccessViolation/Segfault during shutdown.
I managed to capture a memory dump and found a few things:
- The shutdown is initiated by a call to
Environment.Exit(0) - During shutdown, the
System.AppContext::OnProcessExitevent is raised, and our event handler tries to send stuff via HTTP System.Net.Http.HttpClientHandler.SendAsynctries to callSystem.Net.Http.SocketsHttpHandler.SendAsync- During the method call, the execution jumps to address 0x0 and causes the crash
The method call in HttpClientHandler.SendAsync looks like:
0ff772e3 8b4da4 mov ecx,dword ptr [ebp-5Ch]
0ff772e6 8b55e0 mov edx,dword ptr [ebp-20h]
0ff772e9 8b45a4 mov eax,dword ptr [ebp-5Ch]
0ff772ec 8b00 mov eax,dword ptr [eax]
0ff772ee 8b4028 mov eax,dword ptr [eax+28h]
0ff772f1 ff5014 call dword ptr [eax+14h]
As far as I can tell, mov eax,dword ptr [ebp-5Ch] moves the address of the HttpClientHandler to eax. Then mov eax,dword ptr [eax] reads the method table, and the next two instructions fetch the destination of the call (I'm not familiar with the layout of the method table).
call dword ptr [eax+14h] ends up calling the address 0ff7c990 which is just a jump:
0:011> !U 0ff7c990
Unmanaged code
0ff7c990 e94b050100 jmp 0ff8cee0
Then 0ff8cee0 contains the tiered jit stub:
0:011> !U 0ff8cee0
Unmanaged code
0ff8cee0 b884bd3d0f mov eax,0F3DBD84h
0ff8cee5 66ff08 dec word ptr [eax]
0ff8cee8 0f85123107f0 jne 00000000
0ff8ceee e8fff36540 call coreclr!OnCallCountThresholdReachedStub (505ec2f2)
Notice the jne 00000000, which causes the access violation.
A few things to note:
- We rejitted
HttpClientHandler.SendAsync:
0:011> !DumpMD /d 0d15a570
Method Name: System.Net.Http.HttpClientHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
Class: 0d168388
MethodTable: 0d15a67c
mdToken: 0600029D
Module: 0d156d20
IsJitted: yes
Current CodeAddr: 0ff77258
Version History:
ILCodeVersion: 0CDD5E30
ReJIT ID: 4
IL Addr: 0ff57550
CodeAddr: 0ff77258 (QuickJitted)
NativeCodeVersion: 0CE8F538
ILCodeVersion: 00000000
ReJIT ID: 0
IL Addr: 7819b150
CodeAddr: 78126a30 (ReadyToRun)
NativeCodeVersion: 00000000
- We rejitted
SocketsHttpHandler.SendAsync:
0:011> !DumpMD /d 0d15d558
Method Name: System.Net.Http.SocketsHttpHandler.SendAsync(System.Net.Http.HttpRequestMessage, System.Threading.CancellationToken)
Class: 0d16a568
MethodTable: 0d15d58c
mdToken: 0600085A
Module: 0d156d20
IsJitted: yes
Current CodeAddr: 0ffb0010
Version History:
ILCodeVersion: 0CDD5890
ReJIT ID: 6
IL Addr: 0ff5be48
CodeAddr: 0ffb0010 (QuickJitted)
NativeCodeVersion: 0CE90488
ILCodeVersion: 00000000
ReJIT ID: 0
IL Addr: 781c3258
CodeAddr: 7816a5b0 (ReadyToRun)
NativeCodeVersion: 00000000
I'm not sure where to go from there. If I add a Thread.Sleep at the beginning of the test then I can't reproduce the issue anymore, so the timing seems important. It's possible that we have a bug in our profiler and we don't handle rejitting correctly, but even in that case I feel like the JIT should never end up writing an empty address in the stub.
I can't provide a minimal repro because of the number of variables involved (and the fact that it depends on the profiling API makes it even harder). However I have a memory dump that I can share, and I can reproduce the issue fairly consistently on my machine.
Reproduction Steps
Expected behavior
The rejitted method should be called normally.
Actual behavior
Invocation crashes with an access violation.
Regression?
No response
Known Workarounds
No response
Configuration
- .NET 6.0.10
- x86 / ARM64
Other information
No response