1.7.5: Occasional failure of CreatePipe or signal handing due to thread-unsafe code in cwdstuff::set

John Carey aeolus.electric-cloud.com@electric-cloud.usa.net
Tue Aug 10 00:21:00 GMT 2010

After a call to SetCurrentDirectory(), I have seen occasional (< 5%) failures of CreatePipe() with code ERROR_INVALID_HANDLE.  I have also seen failure of Cygwin signal handling because the signal handling pipe has mysteriously closed.

I believe that both symptoms are due to this code in winsup/cygwin/path.cc, which as the comments state, is not thread-safe:

      /* Workaround a problem in Vista/Longhorn which fails in subsequent
         calls to CreateFile with ERROR_INVALID_HANDLE if the handle in
         CurrentDirectoryHandle changes without calling SetCurrentDirectory,
         and the filename given to CreateFile is a relative path.  It looks
         like Vista stores a copy of the CWD handle in some other undocumented
         place.  The NtClose/DuplicateHandle reuses the original handle for
         the copy of the new handle and the next CreateFile works.
         Note that this is not thread-safe (yet?) */
      NtClose (*phdl);
      if (DuplicateHandle (GetCurrentProcess (), h, GetCurrentProcess (), phdl,
                           0, TRUE, DUPLICATE_SAME_ACCESS))
        NtClose (h);

If another thread allocates a handle between the NtClose and the Duplicate, then the handle value is not preserved, and strange effects may result.  Presumably the issues mentioned in the source comment may also occur, but what I have seen myself is a subsequent call to SetCurrentDirectory() closing, not the current directory handle, but the handle that was allocated by the other thread.

Specifically, dll_crt0_1() calls sigproc_init(), then cwdstuff::set(), and finally wait_for_sigthread().  The function sigproc_init() creates the signal handling thread, which creates the signal handling pipe as
one of its very first actions, then sets the event for which wait_for_sigthread() waits.  I think this scenario happens:

  1. main thread: cwdstuff::set(): NtClose().

  2. signal handling thread: CreatePipe() opens a handle to '\Device\NamedPipe\' and stores it in a global variable (because this is the first call to CreatePipe()).

  3. main thread: cwdstuff::set(): DuplicateHandle().

In this case, the current directory handle value has changed, which is not the intend of the NtClose-Duplicate sequence.  Perhaps it causes CreateFile to fail with ERROR_INVALID_HANDLE as mentioned in the source comments, but I have not seen that.  I think that the CreatePipe() failures I have seen are triggered when SetCurrentDirectory() closes the handle to '\Device\NamedPipe\', thinking that it is the current directory handle.  After that, CreatePipe() will fail with ERROR_INVALID_HANDLE.

I think this other scenario also happens:

  1. signal handling thread: CreatePipe() opens a handle to '\Device\NamedPipe\' (because it is the first call to CreatePipe()).

  2. main thread: cwdstuff::set(): NtClose().

  3. signal handling thread: CreatePipe() opens pipe handles.

  4. main thread: cwdstuff::set(): DuplicateHandle().

In this case it is Cygwin signal handling that is sabotaged by subsequent calls to SetCurrentDirectory(), because they close one of the pipe handles used for Cygwin signal handling.

Note that replacing calls to SetCurrentDirectory() with chdir() could actually make the problem worse, because it would trigger the thread-unsafe code more frequently.  The call to SetCurrentDirectory() is merely making it possible to detect the problem sooner.

Though this would not eliminate the problem entirely, would it be possible to better synchronize the signal handling thread during Cygwin initialization, either by delaying creation of that thread until the first cwdstuff::set(), or by calling wait_for_sigthread() before cwdstuff::set()?

-- John

Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple

More information about the Cygwin mailing list