aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2018-07-10 14:13:15 +0200
committerCorinna Vinschen <corinna@vinschen.de>2018-07-10 14:13:15 +0200
commit698d93c4b4edb442ca6ebae13c29e2d14feb047e (patch)
treea79740a173fc70e0ba11043bd17f249e03071bc5
parent3a6833e3c4658a03b19d4fa820094b983e37500c (diff)
downloadnewlib-698d93c4b4edb442ca6ebae13c29e2d14feb047e.zip
newlib-698d93c4b4edb442ca6ebae13c29e2d14feb047e.tar.gz
newlib-698d93c4b4edb442ca6ebae13c29e2d14feb047e.tar.bz2
Cygwin: fix a race in the FAST_CWD fallback code
Guard the entire operation with the FastPebLock critical section used by RtlSetCurrentDirectory_U as well, thus eliminating the race between concurrent chdir/fchdir/SetCurrentDirectory calls. Streamline comment explaining the fallback method. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
-rw-r--r--winsup/cygwin/path.cc39
1 files changed, 17 insertions, 22 deletions
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
index d54ea1a..d56f22e 100644
--- a/winsup/cygwin/path.cc
+++ b/winsup/cygwin/path.cc
@@ -4390,43 +4390,38 @@ cwdstuff::override_win32_cwd (bool init, ULONG old_dismount_count)
}
else
{
- /* This is more a hack, and it's only used if we failed to find the
- fast_cwd_ptr value. We call RtlSetCurrentDirectory_U and let it
- set up a new FAST_CWD structure. Afterwards, compute the address
- of that structure utilizing the fact that the buffer address in
- the user process parameter block is actually pointing to the buffer
- in that FAST_CWD structure. Then replace the directory handle in
- that structure with our own handle and close the original one.
+ /* Fallback if we failed to find the fast_cwd_ptr value:
- Note that the call to RtlSetCurrentDirectory_U also closes our
- old dir handle, so there won't be any handle left open.
+ - Call RtlSetCurrentDirectory_U.
+ - Compute new FAST_CWD struct address from buffer pointer in the
+ user process parameter block.
+ - Replace the directory handle in the struct with our own handle.
+ - Close the original handle. RtlSetCurrentDirectory_U already
+ closed our former dir handle -> no handle leak.
- This method is prone to two race conditions:
+ Guard the entire operation with FastPebLock to avoid races
+ accessing the PEB and FAST_CWD struct.
- - Due to the way RtlSetCurrentDirectory_U opens the directory
- handle, the directory is locked against deletion or renaming
- between the RtlSetCurrentDirectory_U and the subsequent NtClose
- call.
+ Unfortunately this method is still prone to a directory usage
+ race condition:
- - When another thread calls SetCurrentDirectory at exactly the
- same time, a crash might occur, or worse, unrelated data could
- be overwritten or NtClose could be called on an unrelated handle.
-
- Therefore, use this *only* as a fallback. */
+ - The directory is locked against deletion or renaming between the
+ RtlSetCurrentDirectory_U and the subsequent NtClose call. */
+ if (unlikely (upp_cwd_hdl == NULL) && init)
+ return;
+ RtlEnterCriticalSection (peb.FastPebLock);
if (!init)
{
NTSTATUS status =
RtlSetCurrentDirectory_U (error ? &ro_u_pipedir : &win32);
if (!NT_SUCCESS (status))
{
+ RtlLeaveCriticalSection (peb.FastPebLock);
debug_printf ("RtlSetCurrentDirectory_U(%S) failed, %y",
error ? &ro_u_pipedir : &win32, status);
return;
}
}
- else if (upp_cwd_hdl == NULL)
- return;
- RtlEnterCriticalSection (peb.FastPebLock);
fcwd_access_t::SetDirHandleFromBufferPointer(upp_cwd_str.Buffer, dir);
h = upp_cwd_hdl;
upp_cwd_hdl = dir;