5

I'm trying to pass a mutex handle, to a child process trough command line, or any other way.

How can I do that? How do I acess the mutex from the child?

This is how I'm creating the child process:

HANDLE ghMutex;

     if( !CreateProcess( _T("C:\\Users\\Kumppler\\Documents\\Visual Studio 2010\\Projects\\teste3\\Debug\\teste3.exe"),   // No module name (use command line)
                aux2,                              // Command line
                NULL,                              // Process handle not inheritable
                NULL,                              // Thread handle not inheritable
                TRUE,                              // Set handle inheritance to TRUE
                STARTF_USESTDHANDLES,              // inherit the standard input, standard output, and standard error handles
                NULL,                              // Use parent's environment block
                NULL,                              // Use parent's starting directory 
                &si[j],                            // Pointer to STARTUPINFO structure
                &pi[j] )                           // Pointer to PROCESS_INFORMATION structure
            )                     

EDIT:

I need to use the mutex for more than one child process, is it ok?

So here is what I'm doing right now:

HANDLE ghMutex;
int mutex;
char mutexstring[7];

mutex=(int)ghMutex;
itoa(mutexValue,mutexString,10);

I'll pass the mutexString trough command line, and then convert it back at child process:

mutexValue=atoi(argv[2]);

Mutex=(HANDLE)mutexValue;

My question, is it okay to do the (HANDLE) casting??

Caio
  • 63
  • 1
  • 4

2 Answers2

8

Two options:

  1. You can use named objects. Process A creates the Mutex with a name and then spawns Process B. Process B then calls OpenMutex or CreateMutex with the same name, and it will get a handle to the same mutex.

    Drawbacks are in name selection. If you have a name collision, you can get unpredictable results. An attacker could create a mutex with the same name and create a denial of service situation. One way to deal with this is to generate the name randomly. For example, Process A could generate a GUID for the name, and then pass that GUID (as a string) on the command line to Process B.

  2. You can use inheritance. Child processes can inherit many types of handles from the parent process, including mutex handles. Set the bInheritHandles parameters in the CreateProcess command (which your sample is already doing), and pass the value of the handle (as a string) on the command line to the child process. The child process can then convert the command line string back to a value and simply start using it. The value is the same in both processes.

This technique doesn't have the same drawbacks as the named object technique.

A working example of inheritance (error checking elided):

#include <cstddef>
#include <iostream>
#include <string>
#include <sstream>
#include <windows.h>

void DoParentWork() {
  std::wcout << L"Parent:  Creating an inheritable event..." << std::endl;
  SECURITY_ATTRIBUTES security = {
    sizeof(security), nullptr, /* bInheritHandle = */ TRUE
  };
  HANDLE hEvent = ::CreateEventW(&security, /* bManualReset = */ TRUE,
                                 /* bInitialState = */ FALSE, nullptr);

  std::wstringstream ssCommand;
  ssCommand << L"foo.exe " << reinterpret_cast<std::size_t>(hEvent);
  std::wstring strCmd = ssCommand.str();;

  std::wcout << L"Parent:  Starting child process..." << std::endl;
  STARTUPINFO start_info = {sizeof(start_info)};
  PROCESS_INFORMATION proc_info = {0};
  ::CreateProcessW(L"foo.exe", &strCmd[0], nullptr, nullptr,
                   /* bInheritHandles = */ TRUE, 0, nullptr, nullptr,
                   &start_info, &proc_info);
  ::CloseHandle(proc_info.hThread);
  ::CloseHandle(proc_info.hProcess);

  std::wcout << L"Parent:  Waiting for the child to signal the event."
             << std::endl;
  if (::WaitForSingleObject(hEvent, 10*1000) == WAIT_OBJECT_0) {
    std::wcout << L"Parent:  The event was signaled." << std::endl;
  } else {
    std::wcout << L"Parent:  Timed out waiting for the event."
               << std::endl;
  }
  ::CloseHandle(hEvent);
}

void DoChildWork(const char *pszEvent) {
  std::stringstream ss(pszEvent);
  UINT_PTR iEvent;
  ss >> iEvent;
  HANDLE hEvent = reinterpret_cast<HANDLE>(iEvent);
  std::cout << "Child:  Event handle "
            << reinterpret_cast<std::size_t>(hEvent) << std::endl;
  ::Sleep(2000);
  std::cout << "Child:  Signalling the event." << std::endl;
  ::SetEvent(hEvent);
}

int main(int cArgs, char *ppszArgs[]) {
  if (cArgs > 1) DoChildWork(ppszArgs[1]);
  else DoParentWork();
  return 0;
}
Community
  • 1
  • 1
Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • +1 for named mutexes. They are the obvious solution. Passing a GUID through the command line to avoid collision is overkill: Just hardcode it in every program: They are unique anyway. – Serge Wautier Sep 14 '11 at 20:42
  • "The child process can then convert the command line string back to a value and simply start using it" You mean convert it to a string? – Caio Sep 15 '11 at 16:31
  • @Serge - appTranslator: Hardcoding the names has two problems. (1) It limits you to a single instance of your application. (2) Denial of service vulnerability if an attacker creates the mutex before you (as noted in my answer). – Adrian McCarthy Sep 15 '11 at 20:39
  • @Caio: I'm not sure what you're asking. I believe I said what I meant. – Adrian McCarthy Sep 15 '11 at 20:39
  • @Adrian McCarthy: I´ll try to explain. You mean that I have to convert the argv[i] back to the value of the mutex variable ( the address) ? – Caio Sep 16 '11 at 13:27
  • @Caio: Sorry, I'm still confused by your questions. Where does an address come into it? The parent process needs to take the HANDLE of the mutex, convert its value to something it can pass on the command line, and the child process needs to convert it back from a string to a value, and cast that value to a HANDLE. – Adrian McCarthy Sep 16 '11 at 16:19
  • @AdrianMcCarthy, converting a handle to a string, passing it to the child and converting it back to a handle does not work. I've tried this and it fails. The child might inherit the handle but the actual numerical value of the handle is different in the child process. – jcoffland Sep 17 '15 at 22:16
  • @jcoffland: MSDN says, "An inherited handle refers to the same object in the child process as it does in the parent process. It also has **the same value**...." It works for me. Make sure you're using SECURITY_ATTRIBUTES with bInheritHandle set to TRUE when the parent creates the handle. https://msdn.microsoft.com/en-us/library/ms683463(v=VS.85).aspx – Adrian McCarthy Sep 18 '15 at 18:26
1

Either create the mutex before creating the child process and make it inheritable (set bInheritHandle to TRUE in the lpMutexAttributes parameter for CreateMutex). This way you can pass the handle in the command line.

Or use DuplicateHandle and pass the handle via some other mechanism (e.g. use a pipe as STDIN for the child process).

John
  • 5,561
  • 1
  • 23
  • 39
  • This does not work. You cannot serialize the HANDLE as a string and pass it to the child. I've tried to do this but it does not work. Feel free to prove me wrong with a working example. – jcoffland Sep 17 '15 at 22:17
  • 1
    @jcoffland sure you can, you do need to duplicate it though, as per answer – David Heffernan Dec 02 '16 at 06:07