Hidden issues with inheritable handles in Windows

In Windows it is very simple to make a child process to inherit a handle. All you need to specify bInheritHandle to TRUE in SecurityAttributes or call this function:

SetHandleInformation(handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)

After that pass TRUE for bInheritHandles parameter of CreateProcess function. After that, the handle will be inherited by the child process. You can use the same handle value in the child process. Usually, you can pass it via command line to the child process.

Most developers would never use it, but handle inheritance is required to capture output of console applications. As a result a lot of developers have to deal with handle inheritance and they usually copy/paste this example from Microsoft.

At first glance there is nothing bad and for most applications it is true. But in fact, there are few huge issues. Let me explain them in detail and with examples. In all examples below CreateProcess will be called with TRUE for the bInheritHandles parameter.

Issue #1

As soon as you make the handle inheritable, then any call to CreateProcess in any thread of this process will create a process that will also inherit this handle. Let me show a specific example.

Process P1 in thread T1 creates handle H1 and then creates process P2. Process P2 will inherit handle H1. Until this point everything is correct. But imaging there is a second thread T2 that creates process P3. Process P3 will also inherit H1. For most handles it is not a problem, but some handles are quite specific. If you check that example from Microsoft, you will find that there are 2 pairs of pipes created. One to write to by child process P2 and one to read from by parent process P1. And eventually the child process quits and as result closes the handle and that is a signal to a parent that there is no more data. But if that handle was unintentionally inherited to another process P3, that process may work much longer and as a result the parent process will wait much longer for that handle to close.

To make this matter worse, it is possible that T2 will create process P3 randomly. And there is only some window during which handles are alive and inheritable. And P3 in turn can have variable run time. This makes debugging this issue extremely hard because 99.99% of time everything works fine.

Issue #2

Imaging P1 will create inheritable handle H1, then create process P2. Process P2 in turn will create process P3. And P3 will also inherit H1. Even if P2 quits, but P3 will continue to keep that handle. And again, if P2 will start P3 only sometimes, then it will be really hard to find why everything is stuck.

Issue #3

After a handle was made inheritable, it will stay inheritable until you close it or call SetHandleInformation and remove inheritance.

Solution

Ok, I explained problems, but how can I fix my code to avoid these problems? If you have source code for all parts of your applications that create processes, then the solution is relatively simple.

Firstly, functions that create new processes should be in a critical section, so only one thread can create processes. This will solve Issue #1.

Secondly, create a new process suspended. Then, while the process is still suspended, close all inherited handles. For non-pipe handles, just remove inheritance flag by calling this:

SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0)

This will fix Issue #2 and Issue #3.

Unfortunately, it is not possible to fix in general. For example, there could be some 3rd party library that creates processes, and you do not have source code for it.

Anyway, I hope it helps someone. It took me almost 2 years to find and understand this problem. And recently I found exactly the same problem in different application and I decided to share this.

Some links that provide more explanation about this problem:
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-7147084
https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/315939

Comments

Post comment