Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Diagnosing Timer Callback Crashes During .NET Process Shutdown

Tech 4

Exception code c0020001 typically indicates RPC_E_SYS_CALL_FAILED, though this description rarely clarifies the actual failure mechanism. When examining a memory dump where an application terminates unexpected during shutdown, the initial investigation focuses on the exception context.

0:040> !analyze -v

CONTEXT:  (.ecxr)
eax=0afdf5dc ebx=0698ade8 ecx=00000001 edx=00000000 esi=0698ade8 edi=7eec0000
eip=7753c5af esp=0afdf5dc ebp=0afdf62c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
KERNELBASE!RaiseException+0x58:
7753c5af c9              leave

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 7753c5af (KERNELBASE!RaiseException+0x00000058)
   ExceptionCode: c0020001
  ExceptionFlags: 00000001
NumberParameters: 1
   Parameter[0]: 8007042b

PROCESS_NAME:  application.exe

The stack trace reveals the execution path leading to the fault:

0:040> k
 # ChildEBP RetAddr      
00 0afdf62c 70e75e0b     KERNELBASE!RaiseException+0x58
01 0afdf648 70f63bf5     clr!COMPlusThrowBoot+0x1a
02 0afdf654 70b6f1da     clr!UMThunkStubRareDisableWorker+0x25
03 0afdf67c 77a9571e     clr!UMThunkStubRareDisable+0x9
04 0afdf6bc 77a80f0b     ntdll!RtlpTpTimerCallback+0x7a
05 0afdf6e0 77a809b1     ntdll!TppTimerpExecuteCallback+0x10f
06 0afdf830 75c4344d     ntdll!TppWorkerThread+0x562
07 0afdf83c 77a69802     kernel32!BaseThreadInitThunk+0xe
08 0afdf87c 77a697d5     ntdll!__RtlUserThreadStart+0x70

The thread originates from the Windows Thread Pool timer mechanism, attempting to transition into managed code through an unmanaged thunk. The CLR explicitly raises an exception within UMThunkStubRareDisableWorker when detecting an invalid runtime state.

Examining the CLR source logic explains this behavior:

extern "C" VOID __stdcall UMThunkStubRareDisableWorker(
    Thread* currentThread, 
    UMEntryThunk* entryThunk, 
    Frame* transitionFrame)
{
    // Validate runtime availability before managed code execution
    if (!IsRuntimeActive())
    {
        currentThread->m_fPreemptiveGCDisabled = 0;
        COMPlusThrowBoot(E_PROCESS_SHUTDOWN_REENTRY);
    }
}

BOOL IsRuntimeActive(BOOL userErrorContext, HINSTANCE instance)
{
    // Block execution during runtime termination
    if (g_fForbidEnterEE == TRUE)
        return FALSE;

    // Prevent execution during finalization phase unless on finalizer thread
    if ((g_fEEShutDown & ShutDown_Finalize2) && 
        !GCHeap::GetGCHeap()->IsCurrentThreadFinalizer())
        return FALSE;

    // Verify essential runtime objects exist
    if (g_pPreallocatedOutOfMemoryException == NULL)
        return FALSE;

    return TRUE;
}

The variable g_fForbidEnterEE controls access to the Execution Engine. Verification within the dump confirms the runtime is shutting down:

0:040> dp clr!g_fForbidEnterEE L1
712a2684  00000001

A value of 1 indicates the CLR has entered shutdown, typically triggered by Environment.Exit or process termination. The main thread stack confirms this state:

0:000> k
00 0028d3b0 77549cd4     ntdll!NtQueryAttributesFile+0x12
...
39 0028ebc0 70d2684b     clr!WaitForEndOfShutdown_OneIteration+0x81
3a 0028ebc8 70d300e2     clr!WaitForEndOfShutdown+0x1b
3b 0028ec08 70d1329e     clr!EEShutDown+0xad
3c 0028ec14 70d132fb     clr!HandleExitProcessHelper+0x4d
3d 0028ec70 70d2ff99     clr!EEPolicy::HandleExitProcess+0x50
3e 0028ec70 7115af3b     clr!ForceEEShutdown+0x31
3f 0028ec70 702a9faf     clr!SystemNative::Exit+0x4f

To identify the specific managed method attempting execution, inspect the UMEntryThunk structure:

class UMEntryThunk
{
private:
    const BYTE* m_pManagedTarget;  // Managed method entry point
    PTR_MethodDesc m_pMD;          // Method descriptor for profiling
};

Locating this structure in the crash dump:

0:040> kb 5
 # ChildEBP RetAddr      Args to Child              
00 0afdf62c 70e75e0b     c0020001 00000001 00000001 KERNELBASE!RaiseException+0x58
01 0afdf648 70f63bf5     006e0fe0 0afdf67c 70b6f1da clr!COMPlusThrowBoot+0x1a
02 0afdf654 70b6f1da     0698ade8 00580a38 0698ade8 clr!UMThunkStubRareDisableWorker+0x25

0:040> dp 00580a38 L2
00580a38  00386580 008f2eb8

0:040> !U 00386580
Unmanaged code
00386580 e9ab390000      jmp     00389f30

0:040> !ip2md 00389f30
MethodDesc:   0018af94
Method Name:  Application.TimerCallback(IntPtr, Boolean)
Class:        00435a7c
MethodTable:  0018afd8
mdToken:      06000034
Module:       0018a6a8
IsJitted:     yes
CodeAddr:     00389f30

The managed method represents a callback registered with the Windows Thread Pool timer infrastructure. The failure occurs because this timer fires after the CLR begins shutdown but before the process terminates.

The resolution requires explicit cleanup of timer resources before initiating process exit. Implement IDisposable pattern for components hosting timers, ensuring Dispose calls CloseHandle or equivalent on the timer queue, or use managed Timer classes that properly synchronize with the CLR shutdown sequence.

Note that the internal parameter 8007042b (ERROR_PROCESS_ABORTED) is hardcoded in the CLR and does not indicate a system corruption requiring repair utilities. The assembly confirms this is a constant:

0:000> ub 70f63bf5
clr!UMThunkStubRareDisableWorker+0x7:
70f63bd7 c9              leave
70f63bd8 e8d47fc3ff      call    clr!CanRunManagedCode (70b9bbb1)
70f63bdd 8b7508          mov     esi,dword ptr [ebp+8]
70f63be0 85c0            test    eax,eax
70f63be2 7511            jne     clr!UMThunkStubRareDisableWorker+0x25
70f63be4 b92b040780      mov     ecx,8007042Bh  ; Hardcoded constant
70f63be9 c7460800000000  mov     dword ptr [esi+8],0
70f63bf0 e8f721f1ff      call    clr!COMPlusThrowBoot

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.