tags:

views:

448

answers:

2

There are at least three parts to this problem, so bear with me:

1) CreateProcess has a parameter bInheritHandles, that causes the child process to inherit all of the inheritable handles in the parent process. This option must be set to TRUE to allow the parent to specify stdin, stdout, and stderr handles for the child in the STARTUPINFO parameter.

2) In Win32 deleting and renaming files can fail when there is more than one handle open to the same file.

3) The Microsoft CRT's open() function will by default create inheritable handles. Additionally the file handles created by default suffer from problem 2 above.

This magic combination creates the following operational problem: Library A calls open() and doesn't expect subsequent renames and deletes to fail. Elsewhere in the process another library B is calling CreateProcess with bInheritHandles set to TRUE (to capture stdin/out/err) temporarily creating duplicate handles. Now occasionally library A's file operations fail. Naturally library A and B are maintained by separate people. I also know of another library A' that uses open() and suffers from a similar problem.

This kb article discusses a related problem and solution. However it still relies on calling CreateProcess with bInheritHandles set to TRUE in the parent process, so it doesn't solve this problem.

I am wondering if others have hit this problem and if there isn't a well known solution?

The kb article above essentially implies that calling CreateProcess with bInheritHandles set to TRUE is racy, so my inclination is to fix library B such that it never does that. I would do this by:

  1. Create a suspended intermediate process (ideally by using rundll to run a custom entry point in library B) with bInheritHandles set to FALSE.
  2. Create stdin/out/err pipes and dup the correct ends of those to the intermediate process.
  3. Pass the duped handles to the intermediate process somehow.
  4. Resume the intermediate process.
  5. From the intermediate process fill out the STARTUPINFO with the pipes from the parent and call CreateProcess with bInheritHandles set to TRUE.

Is this a good strategy or is there some better solution? How would you recommend passing the duped handles to the intermediate process in step 3? Is rundll + custom entry point a reliable way to setup the intermediate process in step 1?

+1  A: 

If you have access to the actual file handles, you can use SetHandleInformation() to remove the HANDLE_FLAG_INHERIT flag before calling CreateProcess().

Remy Lebeau - TeamB
A: 

You can use the ZwQuerySystemInformation(SystemHandleInformation, ...) ntdll.dll function to find all the handles owned by your process, then all SetHandleInformation on each one, as suggested by Remy, to remove the HANDLE_FLAG_INHERIT flag.

atomice
Unfortunately there's still a race as where open is called by another thread between ZwQuerySystemInformation and CreateProcess. Also someone else might depend on a handle being inheritable.
Mark Zeren
The knowledge base article says "Windows will always duplicate the STD handles, even when bInheritHandles is set to FALSE.". Have you tried using SetStdHandle to change the std handles of your process, then calling CreateProcess with bInheritHandles == FALSE. Obviously you would need to wrap it in a critical section and be confident no other thread will try to use stdout, etc. at the same time.
atomice
That's a cute trick! However I'd rather not change stdin/out/err out from under other threads. I'm still leaning towards the approach laid out in the question, but haven't implemented it yet.
Mark Zeren