Hi all,

I just had a familiar (to me) problem reappear:
zombie hooks getting called after program exit, which leads to a crash
upon Wine exit.

It turns out that the module that created the guilty hook
(TOPHOOK.DLL, hMod 0x0287) didn't get cleaned up properly.
It does:
Call USER.121: SETWINDOWSHOOK(0x0005,0x029f0010) ret=029f:02d4 ds=02af
trace:hook:HOOK_SetHook Setting hook 5: 029f0010 0287 00000000
trace:hook:HOOK_SetHook Setting hook 5: ret=0328 [next=0000]
Ret  USER.121: SETWINDOWSHOOK() retval=0x4b480328 ret=029f:02d4 ds=02af


TOPHOOK.DLL gets loaded here:
trace:module:CreateProcessA tidy_cmdline: name 'C:\WRK\UTIL\TOPDESK.EXE'[23], cmdline 
''[0]
trace:module:NE_LoadExeHeader Using fast-load area offset=800 len=32256
trace:module:NE_LoadExeHeader Converting entry table.
trace:module:NE_LoadExeHeader entry table: offs 0175, len 0027, entries 6
trace:module:NE_LoadExeHeader first bundle: 0x40a71cf5
trace:module:GetModuleHandle16 (KERNEL)
trace:task:TASK_Create module='topdesk' cmdline='' task=025f
trace:module:NE_LoadDLLs Loading 'TOPHOOK'
trace:module:GetModuleHandle16 (TOPHOOK)
trace:module:MODULE_GetLoadOrder Looking for 'TOPHOOK.DLL' (TOPHOOK), found '<nothing>'
.
.
.
trace:module:NE_OpenFile opened 'C:\WINDOWS\TOPHOOK.DLL' -> 1414155699


Upon program exit that happens:
Call KERNEL.102: DOS3CALL() ret=0277:516a ds=027f
     AX=4c00 BX=0078 CX=0000 DX=0b7a SI=06e8 DI=06e8 ES=027f EFL=00000202
.
.
.
trace:task:TASK_KillTask Killing task 025f ("topdesk")
trace:hook:USER_ModuleUnload (022f)
trace:module:NE_FreeModule 022f wep 1 count 1
trace:module:NE_FreeModule 0287 wep 0 count 1
trace:module:NE_FreeModule 00de wep 0 count 3
trace:module:NE_FreeModule 01e6 wep 0 count 3
trace:module:NE_FreeModule 02b6 wep 0 count 1
trace:module:NE_FreeModule 00de wep 0 count 2
trace:module:NE_FreeModule 01c6 wep 0 count 2
trace:module:NE_FreeModule 01e6 wep 0 count 2
trace:task:TASK_Reschedule entered with hCurrentTask 025f by hTask 025f (pid 19659)
trace:task:TASK_Reschedule      task = 01af, events = 1
trace:task:TASK_Reschedule Switching to task 01af (KERNEL)
Ret  USER32.270: GetMessageA() retval=00000001 ret=080499e5 fs=008f
Call USER32.556: TranslateMessage(40a1fde0) ret=080499bb fs=008f
Ret  USER32.556: TranslateMessage() retval=00000000 ret=080499bb fs=008f
Call USER32.141: DispatchMessageA(40a1fde0) ret=080499c3 fs=008f
Ret  USER32.141: DispatchMessageA() retval=00000000 ret=080499c3 fs=008f
trace:module:MODULE_InitDll (USER32.DLL,PROCESS_DETACH,0x1) - CALL
trace:relay:PE_InitDLL CallTo32(entryproc=0x40398bd0,module=40ee0000,type=0,res=0x1)
trace:module:MODULE_InitDll (USER32.DLL,PROCESS_DETACH,0x1) - RETURN 1
trace:module:MODULE_InitDll (GDI32.DLL,PROCESS_DETACH,0x1) - CALL
trace:relay:PE_InitDLL CallTo32(entryproc=0x40398afc,module=40dc0000,type=0,res=0x1)
trace:module:MODULE_InitDll (GDI32.DLL,PROCESS_DETACH,0x1) - RETURN 1
trace:module:MODULE_InitDll (KERNEL32.dll,PROCESS_DETACH,0x1) - CALL
trace:relay:PE_InitDLL CallTo32(entryproc=0x40398820,module=40d90000,type=0,res=0x1)
trace:module:MODULE_InitDll (KERNEL32.dll,PROCESS_DETACH,0x1) - RETURN 1
trace:module:MODULE_InitDll (ntdll.dll,PROCESS_DETACH,0x1) - CALL
trace:module:MODULE_InitDll (ntdll.dll,PROCESS_DETACH,0x1) - RETURN 1
trace:task:TASK_KillTask Killing task 01af
trace:hook:HOOK_CallHook Calling hook 0328: 4 00000358 00000000
CallTo16(func=029f:0010,ds=0097,0x0004,0x0358,0x0000,0x0000) ss:sp=0097:ffd0
Unhandled exception: priviledged instruction in 32-bit code (0x40395eee).
Loading symbols: /usr/local/src/wine/wine

And there we are.
(0x0297:0x0010 isn't valid any more)

Task 0x025f that gets killed is the program named "topdesk" which referenced
the TOPHOOK.DLL.

According to above, we do a USER_ModuleUnload of 0x022f (what the hell
 is this ?) and after that we do a NE_FreeModule of it which causes
recursive NE_FreeModule()s of other module, *all* of them with call_wep
set to 0 !!!

But in NE_FreeModule() we have this code:
    if (call_wep && !(pModule->flags & NE_FFLAGS_WIN32))
    {
        if (pModule->flags & NE_FFLAGS_LIBMODULE)
        {
            MODULE_CallWEP( hModule );

            /* Free the objects owned by the DLL module */
            TASK_CallTaskSignalProc( USIG16_DLL_UNLOAD, hModule );
            PROCESS_CallUserSignalProc( USIG_DLL_UNLOAD_WIN16, 0, hModule );
        }
        else
            call_wep = FALSE;  /* We are freeing a task -> no more WEPs */
    }

So obviously upon first entry the
        else
            call_wep = FALSE;  /* We are freeing a task -> no more WEPs */
gets triggered as this is a task.

Then the NE_FreeModule() loop further below gets called with call_wep set
to FALSE.

This however prevents further TASK_CallTaskSignal... and *especially*
PROCESS_CallUserSignalProc(...) from being called (which is responsible
for hook unloading !!).
So all DLLs referenced by a task seem to have their hooks not properly
unregistered upon task exit.

-> zombie hooks getting called -> crash.

I therefore think that calling TASK_CallTaskSignalProc and
PROCESS_CallUserSignalProc only within the WEP handling is blatantly wrong.
Or at least the hooks should be cleaned up somehow independently from the
WEP handling...

Comments ?

(I'd have loved posting a patch already, but then I'm no expert at all
about the whole thing here...)

Andreas Mohr

Reply via email to