Cygwin: AF_UNIX: make sure connect wait thread is cleanly interruptible

Using TerminateThread potentially leaks resources.  In our case,
the connect wait thread may be forcefully terminated after
having successfully opened a client side pipe handle.  If this
occurs, we have a stale pipe server instance, so the pipe will
never be closed as long as the process lives.

Avoid this by changing the npfs handle to non-blocking, so we can
wait on a termination event object from inside the thread itself
and cleanly exit from the thread instead of terminating.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2018-03-07 16:19:32 +01:00
parent 27a63d4ef2
commit cde2648c22
2 changed files with 49 additions and 8 deletions

View File

@ -860,6 +860,7 @@ class fhandler_socket_unix : public fhandler_socket
if the socket is backed by a file in the
file system (actually a reparse point) */
HANDLE connect_wait_thr;
HANDLE cwt_termination_evt;
PVOID cwt_param;
LONG so_error;
sun_name_t *sun_path;

View File

@ -667,7 +667,7 @@ fhandler_socket_unix::npfs_handle (HANDLE &nph)
InitializeObjectAttributes (&attr, &ro_u_npfs, 0, NULL, NULL);
status = NtOpenFile (&npfs_dirh, FILE_READ_ATTRIBUTES | SYNCHRONIZE,
&attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT);
0);
}
ReleaseSRWLockExclusive (&npfs_lock);
if (NT_SUCCESS (status))
@ -808,7 +808,11 @@ fhandler_socket_unix::wait_pipe (PUNICODE_STRING pipe_name)
conn_wait_info_t *wait_info;
DWORD waitret, err;
int ret = -1;
HANDLE thr, evt;
PVOID param;
if (!(cwt_termination_evt = create_event ()))
return -1;
wait_info = (conn_wait_info_t *)
cmalloc_abort (HEAP_FHANDLER, sizeof *wait_info);
wait_info->fh = this;
@ -823,23 +827,28 @@ fhandler_socket_unix::wait_pipe (PUNICODE_STRING pipe_name)
{
cfree (wait_info);
__seterrno ();
return -1;
goto out;
}
if (is_nonblocking ())
{
set_errno (EINPROGRESS);
return -1;
goto out;
}
waitret = cygwait (connect_wait_thr, cw_infinite, cw_cancel | cw_sig_eintr);
if (waitret == WAIT_OBJECT_0)
GetExitCodeThread (connect_wait_thr, &err);
else
TerminateThread (connect_wait_thr, 0);
HANDLE thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
{
SetEvent (cwt_termination_evt);
WaitForSingleObject (connect_wait_thr, INFINITE);
GetExitCodeThread (connect_wait_thr, &err);
waitret = WAIT_SIGNALED;
}
thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
if (thr)
CloseHandle (thr);
PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
param = InterlockedExchangePointer (&cwt_param, NULL);
if (param)
cfree (param);
switch (waitret)
@ -858,6 +867,10 @@ fhandler_socket_unix::wait_pipe (PUNICODE_STRING pipe_name)
ret = 0;
break;
}
out:
evt = InterlockedExchangePointer (&cwt_termination_evt, NULL);
if (evt)
NtClose (evt);
return ret;
}
@ -965,6 +978,7 @@ fhandler_socket_unix::fixup_after_fork (HANDLE parent)
InitializeSRWLock (&bind_lock);
InitializeSRWLock (&io_lock);
connect_wait_thr = NULL;
cwt_termination_evt = NULL;
cwt_param = NULL;
}
@ -1001,6 +1015,7 @@ fhandler_socket_unix::dup (fhandler_base *child, int flags)
InitializeSRWLock (&fhs->bind_lock);
InitializeSRWLock (&fhs->io_lock);
fhs->connect_wait_thr = NULL;
fhs->cwt_termination_evt = NULL;
fhs->cwt_param = NULL;
return fhandler_socket::dup (child, flags);
}
@ -1019,6 +1034,7 @@ DWORD
fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name)
{
HANDLE npfsh;
HANDLE evt;
LONG error = 0;
NTSTATUS status;
IO_STATUS_BLOCK io;
@ -1033,6 +1049,8 @@ fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name)
error = geterrno_from_nt_status (status);
goto out;
}
if (!(evt = create_event ()))
goto out;
pwbuf_size = offsetof (FILE_PIPE_WAIT_FOR_BUFFER, Name) + pipe_name->Length;
pwbuf = (PFILE_PIPE_WAIT_FOR_BUFFER) alloca (pwbuf_size);
pwbuf->Timeout.QuadPart = -20 * NS100PERSEC; /* 20 secs */
@ -1042,8 +1060,22 @@ fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name)
stamp = ntod.nsecs ();
do
{
status = NtFsControlFile (npfsh, NULL, NULL, NULL, &io, FSCTL_PIPE_WAIT,
status = NtFsControlFile (npfsh, evt, NULL, NULL, &io, FSCTL_PIPE_WAIT,
pwbuf, pwbuf_size, NULL, 0);
if (status == STATUS_PENDING)
{
HANDLE w[2] = { evt, cwt_termination_evt };
switch (WaitForMultipleObjects (2, w, FALSE, INFINITE))
{
case WAIT_OBJECT_0:
status = io.Status;
break;
case WAIT_OBJECT_0 + 1:
default:
status = STATUS_THREAD_IS_TERMINATING;
break;
}
}
switch (status)
{
case STATUS_SUCCESS:
@ -1074,6 +1106,9 @@ fhandler_socket_unix::wait_pipe_thread (PUNICODE_STRING pipe_name)
case STATUS_INSUFFICIENT_RESOURCES:
error = ENOBUFS;
break;
case STATUS_THREAD_IS_TERMINATING:
error = EINTR;
break;
case STATUS_INVALID_DEVICE_REQUEST:
default:
error = EIO;
@ -1434,12 +1469,17 @@ fhandler_socket_unix::shutdown (int how)
int
fhandler_socket_unix::close ()
{
HANDLE evt = InterlockedExchangePointer (&cwt_termination_evt, NULL);
HANDLE thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
if (thr)
{
TerminateThread (thr, 0);
if (evt)
SetEvent (evt);
WaitForSingleObject (thr, INFINITE);
CloseHandle (thr);
}
if (evt)
NtClose (evt);
PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
if (param)
cfree (param);