This is more of a point to raise rather than question, as I couldn't find
any previous discussion about it in the archives so thought it'd be good to
I noticed that when removing the PROCESS_TERMINATE flag on some requests, I
was occasionally seeing the process hang before it had chance to properly
A bit of background, ObRegisterCallbacks allows you to register for process
handle request callbacks, which gives us an OB_PRE_CREATE_HANDLE_INFORMATION
structure with the handle information. We can then modify the access mask
before the handle is created, resulting in a handle with reduced rights. The
docs state the following :
"An ACCESS_MASK value that specifies the access rights to grant for the
handle. By default, this member equals OriginalDesiredAccess, but the
ObjectPreCallback routine can modify this value to restrict the access that
"If the access right is listed as a modifiable flag, the access right can be
Removing the PROCESS_TERMINATE flags works well in most scenarios, however
this hang was occurring which seemed to be linked to process elevation.
Here's what's happening:
- Parent process calls CreateProcess to start a child.
- CreateProcessInternalW starts to setup the process object and then
requests a handle to it with PROCESS_ALL_ACCESS
- The child process being created is something we want to stop being
terminated (for some users), so the PROCESS_TERMINATE right is removed and
the process continues its setup.
- The first thread is created in the child process, it does some init
and enters a suspended state.
- CreateProcessInternalW realises that the child process needs
elevating, so it calls TerminateProcess to cleanup the suspended process
that it created.
- TerminateProcess fails because it doesn't have the rights. However
Windows doesn't check the return code and just calls WaitForSingleObject
using the process handle
- The child process remains suspended and never terminates, the handle
is never signalled and parent thread waits indefinitely, so we get a hang in
the parent thread and a zombie process.
There's no way of knowing that this handle request is coming from Windows as
part of the CreateProcess routine (without deciphering the callstack...) so
we can't put in any special case code for this scenario. There's therefore
no easy way to avoid this lockup without putting in more fine grain control
into the rules.
I'm just pointing this out at the docs point to this being a valid thing to
do, however CreateProcessInternal isn't capable of handling this scenario
and just assumes that it will always have the right to terminate. The docs
are missing this piece of information, which breaks this design.
I know some people will be thinking that you should never remove this flag,
and I tend to agree, however enterprise products do occasionally have to do
things like this when large customers request it, and this is one of those
cases. It's a shame because the design is really quite nice otherwise, and
this issue might force people down the detour route instead, which I'm
personally very much against.