WinDbg: access violation exception (0xC0000005) when running the !for_each_module command

This exception occurs when the CommandString parameter for the !for_each_module contains the !address command.

0:000> !for_each_module !address @#Base
c0000005 Exception in ext.for_each_module debugger extension.
      PC: 00000000  VA: 00000000  R/W: 8  Parameter: 00000000
0:018> lmDvmext
    Image name: ext.dll
    Timestamp:        Thu Aug 22 07:11:11 2013 (52158F5F)
    CheckSum:         002EAC1C
    ImageSize:        00300000
    File version:     6.3.9600.16384
    Product version:  6.3.9600.16384
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® Windows® Operating System
    InternalName:     ext.DLL
    OriginalFilename: ext.DLL
    ProductVersion:   6.3.9600.16384
    FileVersion:      6.3.9600.16384 (debuggers(dbg).130821-1623)

From the next excerpts we can see that the !for_each_module command was written by using Debugger Engine API and the WdbgExts API. It obtains the Debugger Engine interfaces in the ext!ExtQuery function and initializes the ExtensionApis global variable in the ext!InitializeGlobals function. Then it calls dbgeng!DebugClient::Execute method to execute the !address command.

0:018> uf .
64a44126 8bff            mov     edi,edi
64a44128 55              push    ebp
64a44129 8bec            mov     ebp,esp
64a4412b 83e4f8          and     esp,0FFFFFFF8h
64a4412e 81ecb40a0000    sub     esp,0AB4h
64a44134 a12430b464      mov     eax,dword ptr [ext!__security_cookie (64b43024)]
64a44139 33c4            xor     eax,esp
64a4413b 898424b00a0000  mov     dword ptr [esp+0AB0h],eax
64a44142 53              push    ebx
64a44143 8b5d0c          mov     ebx,dword ptr [ebp+0Ch]
64a44146 33c0            xor     eax,eax
64a44148 834c241cff      or      dword ptr [esp+1Ch],0FFFFFFFFh
64a4414d 8bcb            mov     ecx,ebx
64a4414f 56              push    esi
64a44150 57              push    edi
64a44151 8b7d08          mov     edi,dword ptr [ebp+8]
64a44154 89442414        mov     dword ptr [esp+14h],eax
64a44158 8d5101          lea     edx,[ecx+1]
64a4415b 89442410        mov     dword ptr [esp+10h],eax
64a4415f 8944242c        mov     dword ptr [esp+2Ch],eax
64a44163 8a01            mov     al,byte ptr [ecx]
64a44165 41              inc     ecx
64a44166 84c0            test    al,al
64a44168 75f9            jne     ext!for_each_module+0x3d (64a44163)  Branch
64a4416a 8364242800      and     dword ptr [esp+28h],0
64a4416f 2bca            sub     ecx,edx
64a44171 0fb7c1          movzx   eax,cx
64a44174 8bcf            mov     ecx,edi
64a44176 89442434        mov     dword ptr [esp+34h],eax
64a4417a e8f460ffff      call    ext!ExtQuery (64a3a273)
64a44638 6a02            push    2
64a4463a 53              push    ebx
64a4463b 6a01            push    1
64a4463d 8b08            mov     ecx,dword ptr [eax]
64a4463f 50              push    eax
64a44640 ff9108010000    call    dword ptr [ecx+108h] ds:002b:6bb92bb0={dbgeng!DebugClient::Execute (6bc1cea4)}
64a44646 ff15a0ccb864    call    dword ptr [ext!ExtensionApis+0x14 (64b8cca0)] <- Access Violation will occur here

0:018> dps ext!ExtensionApis+0x14 L1
64b8cca0  6bd176f2 dbgeng!CheckUserInterrupt
64a3a273 8bff            mov     edi,edi
64a3a275 51              push    ecx
64a3a276 a1f800b964      mov     eax,dword ptr [ext!g_RecursionCount (64b900f8)]
64a3a27b 40              inc     eax
64a3a27c a3f800b964      mov     dword ptr [ext!g_RecursionCount (64b900f8)],eax
64a3a281 56              push    esi
64a3a282 8bf1            mov     esi,ecx
64a3a284 57              push    edi
64a3a285 83f801          cmp     eax,1
64a3a288 7e07            jle     ext!ExtQuery+0x1e (64a3a291)
64a3a28a 33c0            xor     eax,eax
64a3a28c e9ed000000      jmp     ext!ExtQuery+0x10b (64a3a37e)
64a3a291 8b06            mov     eax,dword ptr [esi]
64a3a293 68f4abb764      push    offset ext!g_ExtAdvanced (64b7abf4)
64a3a298 68d8aa9564      push    offset ext!_GUID_f2df5f53_071f_47bd_9de6_5734c3fed689 (6495aad8)
64a3a29d 56              push    esi
64a3a29e ff10            call    dword ptr [eax]      ds:002b:6bb92d6c={dbgeng!DebugClient::QueryInterface (6bc17a48)}
64a3a31f e83afbffff      call    ext!InitializeGlobals (64a39e5e)
64a3a37e 5f              pop     edi
64a3a37f 5e              pop     esi
64a3a380 59              pop     ecx
64a3a381 c3              ret
64a39eae 688cccb864      push    offset ext!ExtensionApis (64b8cc8c)
64a39eb3 56              push    esi
64a39eb4 ff9040010000    call    dword ptr [eax+140h] ds:002b:6bb92be8={dbgeng!DebugClient::GetWindbgExtensionApis64 (6bc1e196)}

Also we can see that the !address command was written by using the EngExtCpp API and that the ExtensionApis global variable is automatically initialized on entry to an EngExtCpp method and cleared on exit.

64aeb1fa 688cccb864      push    offset ext!ExtensionApis (64b8cc8c)
64aeb1ff 50              push    eax
64aeb200 8b08            mov     ecx,dword ptr [eax]
64aeb202 ff9140010000    call    dword ptr [ecx+140h] ds:002b:6bb92be8={dbgeng!DebugClient::GetWindbgExtensionApis64 (6bc1e196)}
0:018> k
 # ChildEBP RetAddr  
00 02d7c428 64aeb79a ext!ExtExtension::Query+0x379
01 02d7c464 649caebd ext!ExtExtension::CallExtCodeSEH+0x1c
02 02d7c4a8 6bc794ca ext!address+0x50
03 02d7c524 6bc7962c dbgeng!ExtensionInfo::CallA+0x1d3
04 02d7c6c8 6bc796a4 dbgeng!ExtensionInfo::Call+0xec
05 02d7c6ec 6bc78763 dbgeng!ExtensionInfo::CallAny+0x4c
06 02d7cb58 6bca9cbb dbgeng!ParseBangCmd+0x439
07 02d7cbc8 6bcaa955 dbgeng!ProcessCommands+0x753
08 02d7cc28 6bc1cdd4 dbgeng!ProcessCommandsAndCatch+0x91
09 02d7d094 6bc1cfc5 dbgeng!Execute+0x226
0a 02d7d0e0 6bc1cf18 dbgeng!DebugClient::ExecuteWide+0x8d
0b 02d7d330 64a44646 dbgeng!DebugClient::Execute+0x74
0c 02d7de08 6bc794ca ext!for_each_module+0x520
64aeb5c3 6a30            push    30h
64aeb5c5 57              push    edi
64aeb5c6 688cccb864      push    offset ext!ExtensionApis (64b8cc8c)
64aeb5cb e8569f0000      call    ext!memset (64af5526)
0:018> r @edi
0:018> k
 # ChildEBP RetAddr  
00 02d7c424 64aeb7e5 ext!ExtExtension::Release+0x333
01 02d7c428 64aeb7ce ext!ExtExtension::CallExtCodeSEH+0x67
02 02d7c464 649caebd ext!ExtExtension::CallExtCodeSEH+0x50
03 02d7c4a8 6bc794ca ext!address+0x50
04 02d7c524 6bc7962c dbgeng!ExtensionInfo::CallA+0x1d3
05 02d7c6c8 6bc796a4 dbgeng!ExtensionInfo::Call+0xec
06 02d7c6ec 6bc78763 dbgeng!ExtensionInfo::CallAny+0x4c
07 02d7cb58 6bca9cbb dbgeng!ParseBangCmd+0x439
08 02d7cbc8 6bcaa955 dbgeng!ProcessCommands+0x753
09 02d7cc28 6bc1cdd4 dbgeng!ProcessCommandsAndCatch+0x91
0a 02d7d094 6bc1cfc5 dbgeng!Execute+0x226
0b 02d7d0e0 6bc1cf18 dbgeng!DebugClient::ExecuteWide+0x8d
0c 02d7d330 64a44646 dbgeng!DebugClient::Execute+0x74
0d 02d7de08 6bc794ca ext!for_each_module+0x520

So, after the execution of the !address command we have an empty ExtensionApis structure, which is still used by ext!for_each_module function.

0:018> r
eax=00000000 ebx=02d7dedc ecx=6bc1cf30 edx=ffffffff esi=80070715 edi=00000000
eip=64a44646 esp=02d7d348 ebp=02d7de08 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
64a44646 ff15a0ccb864    call    dword ptr [ext!ExtensionApis+0x14 (64b8cca0)] ds:002b:64b8cca0=00000000

0:018> dps ext!ExtensionApis+0x14 L1
64b8cca0  00000000

I also took an extra step and found that the !address command was rewritten using the EngExtCpp framework in the Windows Driver Kit 8.0, and that the conjunction of these commands in the previous version of the WDK does not cause the problem.