While working on proper logoff/shutdown processing, I'm running into an
interesting race condition inside kernel32. The shutdown command
(apps/utils/shutdown) calls ExitWindowsEx() and then terminates, which
causes kernel32's DllMain() function to be called with the
DLL_PROCESS_DETACH reason. This will clean up kernel32 resources, like
calling RtlDeleteCriticalSection(&ConsoleLock).
Now, before the process is completely shutdown, CSRSS will send it a
CTRL_LOGOFF_EVENT. To deliver this event, CSRSS calls CreateRemoteThread().
So CreateRemoteThread() is called while the main thread is executing
DLL_PROCESS_DETACH cleanup. During handling of CTRL_LOGOFF_EVENT, the new
thread will try to enter the ConsoleLock critical section, which was already
deleted by the main thread. Chaos results.
To solve this, first I was looking for a way to disable new thread creation
when a process enters ExitProcess(), but that won't solve the problem. We
can still have a thread A which calls ExitProcess(), have a context switch
to existing thread B of the same process which does something that needs the
ConsoleLock. Then I considered SuspendThread()ing all other threads of the
process when ExitProcess() is called, but that could potentially lead to
deadlocks if the suspended threads hold synchronization objects. So now I'm
inclined to believe that the solution is to just not do the resource cleanup
during DLL_PROCESS_DETACH handling. But that doesn't feel very clean either.
Anyone have a better idea?
GvG