Skip to content

AccessViolation when calling rejitted method #77973

@kevingosse

Description

@kevingosse

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::OnProcessExit event is raised, and our event handler tries to send stuff via HTTP
  • System.Net.Http.HttpClientHandler.SendAsync tries to call System.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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions