Debugging walkthrough: Diagnosing an NX exception


A colleague of mine asked for help debugging a strange failure. Execution halted because the CPU detected that it was trying to execute data.

ABC!__PchSym_ (ABC+0x67be4)
user32!UserCallWinProcCheckWow+0x140
user32!DispatchClientMessage+0xa2
user32!__fnDWORD+0x2d
ntdll!KiUserCallbackDispatcherContinue
user32!ZwUserPeekMessage+0xa
user32!PeekMessageW+0x7f
explorerframe!CExplorerFrame::FrameMessagePump+0x5b
explorerframe!BrowserThreadProc+0x5e
explorerframe!BrowserNewThreadProc+0x3a
explorerframe!CExplorerTask::InternalResumeRT+0x12
explorerframe!CRunnableTask::Run+0xc9
shell32!CShellTaskThread::ThreadProc+0x284
shell32!CShellTaskThread::s_ThreadProc+0x2b
SHCore!_WrapperThreadProc+0x15f
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ffcfd197be4 (ABC+0x67be4)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000008
   Parameter[1]: 00007ffcfd197be4
Attempt to execute non-executable address 00007ffcfd197be4

My colleague suspected that a return address got overwritten by some function deeper in the stack, and that caused the instruction pointer to jump to a random module, and the victim module was ABC.

I looked at the crash dump, and came to a different conclusion. The stack is just fine. The problem is that a DLL got unloaded:

0:067> lm
...
Unloaded modules:
...
00007ffc`fd140000 00007ffc`fd1ee000   DEF.dll
...

After DEF.dll got unloaded, ABC.DLL got loaded into the same location.

0:067> .reload /unl DEF.dll
WARNING: DEF overlaps ABC

The problem is that DEF.dll unloaded before destroying all its windows. And then its window received a message (in this case, WM_ACTIVATE­APP, but you were not expected to know this since it wasn't in the stack trace). The window manager called the window procedure, which now points into the middle of ABC.dll. The debugger is correctly reporting that execution halted in the middle of ABC.dll.

The next step is to engage the people responsible for DEF.dll to figure out why they leaked a window.

Exercise: What command would be useful at this point to help the DEF.dll identify the window that they leaked?

Comments (9)
  1. Douglas says:

    Exercise:
    Sorry, my windbg (or similar) -fu is weak.
    Not being all that familiar with these debuggers, I don't know if you need to tell it the function signature in a case like this.
    Then, a quick web search shows that the command is 'dv' (if you have symbols), or possibly 'kp', to find the arguments (aka. "actual parameters") passed to the window procedure.
    From there, you have hwnd and friends. The DEF team might be able to look at their stuff from there. If not, I'm not sure how you would interrogate the window from a crash dump.

  2. Dave says:

    inkedit in a richtext control? I discovered the hard way to only way around this is to unsubclass on WM_DESTROY.

  3. sense says:

    mmm... maybe the window function is specific enough and a "u" will show its name at this point?
    I know, there must be some function to extract the class name from the window handle, which should be placed somewhere on the stack (or heap?) near the WM_ACTIVATEAPP that you found. But that's as far as my knowledge goes for now.

  4. Alois Kraus says:

    It is still a mystery to me how window handles in Windbg are accessible. The Gdikdx.dll extension is only for Windows 2000 and XP. That whole GDI stuff is pretty much inaccessible in Windbg and should be fixed in my opinion. If you only have the stack then you should after unloading the wrong pdb do a lm to see in the unloaded module list DEF.dll and its previous load addresses. Then use the closest matching base address for it to do a .reload DEF.dll=xxxx. Now the stack trace with e.g. kb should display the window method of DEF.dll. I have not tried this but I guess that could work.

  5. If you are a little bit lazy then dps @esp would be enough

  6. exchange development blog team says:

    >Exercise: What command would be useful at this point to help the DEF.dll identify the window that they leaked?

    "google stackoverflow".

  7. John Doe says:

    Exercise: Since DEF.dll was reloaded overlapping ABC.dll, you can get the window handle by inspecting the current arguments. If you're debugging live, you can get the window title, class, etc.

    The command is: dv

  8. alegr1 says:

    It would be helpful having a debug VirtualAlloc mode where the virtual ranges keep reserved when decommitted, so any unloaded DLL range will not get reused.

  9. Andrew Rogers (ex-MSFT) says:

    Note from the exception record that 0007ffcfd197be4 == ABC+0x67be4. Therefore ABC is based at:

    0:000> ?00007ffcfd197be4-0x67be4
    Evaluate expression: 140724554366976 = 00007ffc`fd130000

    Whereas DEF is based at 00007ffc`fd140000.

    Therefore the exception address, ABC+0x67be4, is DEF+0x57be4:

    0:000> ?00007ffc`fd197be4-00007ffc`fd140000
    Evaluate expression: 359396 = 00057be4

    So the next command is 'ln DEF+0x57be4', c.f. the same on ntdll:

    0:000> ln ntdll+0x57be4
    (77f179cd) ntdll!RtlDosApplyFileIsolationRedirection_Ustr+0x2d0 | (77f17f14) ntdll!`string'

    Andrew R

Comments are closed.

Skip to main content