Diagnosing a crash in unloaded_something.dll


A failure report came in to the shell team because Explorer crashed at shutdown in what the debugger reported as unloaded_themeui.dll. Time to dig in.

ntdll!RtlpCallVectoredHandlers+0xeb
ntdll!RtlDispatchException+0x81
ntdll!KiUserExceptionDispatch+0x50
<Unloaded_themeui.dll>+0x2bbfbd
0x1fbdebe0
0x1fbdebc0
0x2357ef80

<Unloaded_themeui.dll>+0x2bbfbd:
00007ff8`e384bfbd ??              ???

Yup, there's nothing loaded there all right. But let's see what was loaded there before.

0:001> lm
...
Unloaded modules:
...
00007ff8`e3590000 00007ff8`e385d000   themeui.dll
00007ff8`e3840000 00007ff8`e385d000   abcdefg.dll

So there were two DLLs that used to be loaded at the address that crashed. Which could it be?

0:001> !reload /unl themeui.dll
0:001> u 00007ff8`e384bfbd
themeui!ext-ms-win-com-ole32-l1-1-1_NULL_THUNK_DATA_DLA <PERF> (themeui+0x2bffdd):
00007ff8`e384bfbd 40              ???

Well, that doesn't look like code. How about abcdefg?

0:001> !reload /unl abcdefg.dll
0:001> u 00007ff8`e384bfbd-80
...
abcdefg!AbcdefgImageList::GetClassImageList+0x1f
00007ff8`e384bfb7 ff1593a20000    call    [abcdefg!_imp_SetupDiGetClassImageList]
00007ff8`e384bfbd 85c0            test    eax,eax

That looks a lot more promising. What appears to have happened is that abcdefg.dll called Setup­Di­Get­Class­Image­List, and while the call was in progress, the DLL got unloaded. When the call to Setup­Di­Get­Class­Image­List finally returned, it returned to an unloaded DLL, which is the source of the crash.

Reconstructing the stack revealed a chain of calls that made sense in the context of abcdefg.dll, so this diagnosis is probably correct. (I've anonymized the name of the other DLL to protect the guilty.)

What happened is that during Explorer startup, abcdefg.dll registered a wait with the thread pool on an event, and at shutdown it unregisters the wait. But it unregisters with the Unregister­Wait function. If a callback is running at the time the wait is unregistered, the function returns ERROR_IO_PENDING, but nobody checks. The shutdown code proceeds to unload abcdefg.dll, and then we are left executing code that was freed.

The code in abcdefg.dll needs to handle the case where the callback is still running at the time the wait is unregistered. You can use the Unregister­Wait­Ex function, which lets you pass an event that is set when the callback completes, or pass INVALID_HANDLE_VALUE to wait synchronously for the callback to complete before returning.

Comments (4)
  1. Raymond, any idea which approach they decided to take?

  2. Yukkuri says:

    ABDCEFG? Clearly this was the hooked on phonics DLL :D

  3. smf says:

    The description of UnregisterWaitEx is so convoluted I’m sure they got it wrong anyway.

    If CompletionEvent is a handle to an event provided by the caller, it is possible for the function to succeed, fail with ERROR_IO_PENDING, or fail with a different error code. If the function succeeds, or if the function fails with ERROR_IO_PENDING, the caller should always wait until the event is signaled to close the event. If the function fails with a different error code, it is not necessary to wait until the event is signaled to close the event.

    Windows XP: If CompletionEvent is a handle to an event provided by the caller and the function fails with ERROR_IO_PENDING, the caller should wait until the event is signaled to close the event. This behavior changed starting with Windows Vista.

  4. Boris says:

    Safari on iOS is finally showing me a font which isn’t Times New Roman.

Comments are closed.

Skip to main content