PRB: Child Inherits Unintended Handles During CreateProcess Call (315939)



The information in this article applies to:

  • Microsoft Win32 Application Programming Interface (API), when used with:
    • the operating system: Microsoft Windows XP
    • the operating system: Microsoft Windows 2000
    • the operating system: Microsoft Windows NT

This article was previously published under Q315939

SYMPTOMS

When you create a child process by using the CreateProcess function call in a multithreaded environment, the child may inherit handles that were not intended to be inherited.

CAUSE

This behavior can occur if two threads simultaneously create child processes and redirect the STD handles through pipes. In this scenario, there is a race condition during the creation of the pipes and processes, in which it is possible for one child to inherit file handles intended for the other child. One thread creates the pipes, and while that thread is in the process of creating the process, the other thread is also creating a child process. All handles that are inheritable in the application during the CreateProcess call are duplicated across to the child process.

RESOLUTION

To work around this issue, wrap the child-creation code in a critical section. This prevents any accidental inheritance. For this method to work properly, create the pipes as noninheritable by setting the security descriptor to NULL. Then, set the ends of the pipe that you want the child to inherit as inheritable by using the SetHandleInformation function call, as demonstrated in the following sample code:
CRITICAL_SECTION    cs;
HANDLE              hReadIn, hWriteIn;
HANDLE              hReadOut, hWriteOut;
HANDLE              hReadErr, hWriteErr;

InitializeCriticalSection(&cs);

EnterCriticalSection(&cs);

if ( !CreatePipe(&hReadIn, &hWriteIn, NULL, 0) )
{
    // an error occurred
}

if ( !CreatePipe(&hReadOut, &hWriteOut, NULL, 0) )
{
    // an error occurred
}

if ( !CreatePipe(&hReadErr, &hWriteErr, NULL, 0) )
{
    // an error occurred
}

if ( !SetHandleInformation(hReadIn, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) )
{
    // an error occurred
}

if ( !SetHandleInformation(hWriteOut, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) )
{
    // an error occurred
}

if ( !SetHandleInformation(hWriteErr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) )
{
    // an error occurred
}

STARTUP_INFO        si;
PROCESS_INFORMATION pi;

si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hReadIn;
si.hStdOutput = hWriteOut;
si.hStdError = hWriteErr;

if ( !CreateProcess( "child.exe",
                     NULL,
                     NULL, NULL,
                     TRUE,
                     NORMAL_PRIORITY_CLASS,
                     lpEnvironment,
                     &si, &pi) )
{
    // an error occurred
}

CloseHandle(hReadIn);
CloseHandle(hWriteOut);
CloseHandle(hWriteErr);

LeaveCriticalSection(&cs);
				
Note that the preceding solution does not come without costs. The creation of critical sections in code is sometimes messy and comes at a price of degraded performance. There is another workaround to this issue, and it requires the creation of an intermediate application to launch the child. However, this solution also has its downside. The main disadvantage to this method is that the parent basically loses the ease with which it obtains the child's process ID. If the parent needs the process ID, the intermediate process must pass it back somehow.

By using this intermediate application method, you avoid any accidental inheritance by relying on Windows to do the work for you. Any accidentally inherited handles in the intermediate process will definitely not be duplicated across to the child. This is guaranteed if you specify FALSE for the bInheritHandles parameter in the call to CreateProcess in the intermediate application. Your pipe handles will still be duplicated because Windows will always duplicate the STD handles, even when bInheritHandles is set to FALSE.

Parent Application

HANDLE              hReadIn, hWriteIn;
HANDLE              hReadOut, hWriteOut;
HANDLE              hReadErr, hWriteErr;

if ( !CreatePipe(&hReadIn, &hWriteIn, NULL, 0) )
{
    // an error occurred
}

if ( !CreatePipe(&hReadOut, &hWriteOut, NULL, 0) )
{
    // an error occurred
}

if ( !CreatePipe(&hReadErr, &hWriteErr, NULL, 0) )
{
    // an error occurred
}

STARTUP_INFO        si;
PROCESS_INFORMATION pi;

si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hReadIn;
si.hStdOutput = hWriteOut;
si.hStdError = hWriteErr;

if ( !CreateProcess( "Intermediate.exe",
                     NULL,
                     NULL, NULL,
                     TRUE,
                     NORMAL_PRIORITY_CLASS,
                     lpEnvironment,
                     &si, &pi) )
{
    // an error occurred
}

CloseHandle(hReadIn);
CloseHandle(hWriteOut);
CloseHandle(hWriteErr);
				

Intermediate Application

STARTUP_INFO        si;
PROCESS_INFORMATION pi;

si.cb = sizeof(si);

if ( !CreateProcess( "child.exe",
                     NULL,
                     NULL, NULL,
                     FALSE,
                     NORMAL_PRIORITY_CLASS,
                     lpEnvironment,
                     &si, &pi) )
{
    // an error occurred
}
				

Modification Type:MajorLast Reviewed:12/18/2003
Keywords:kbAPI kbKernBase kbprb kbThread KB315939