Cygwin: AF_UNIX: fix accept behaviour

* Use correct cygwait/WFSO invocation to not die on cancel and signals
  uncontrolled.
* Manage io handles under io_lock.
* Copy peer address to user space under SEH to avoid a resource leak.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2018-03-07 16:23:44 +01:00
parent cde2648c22
commit 855e5d7e14
1 changed files with 37 additions and 15 deletions

View File

@ -901,6 +901,7 @@ fhandler_socket_unix::listen_pipe ()
NTSTATUS status; NTSTATUS status;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
HANDLE evt = NULL; HANDLE evt = NULL;
DWORD waitret = WAIT_OBJECT_0;
io.Status = STATUS_PENDING; io.Status = STATUS_PENDING;
if (!is_nonblocking () && !(evt = create_event ())) if (!is_nonblocking () && !(evt = create_event ()))
@ -909,12 +910,18 @@ fhandler_socket_unix::listen_pipe ()
FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0); FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0);
if (status == STATUS_PENDING) if (status == STATUS_PENDING)
{ {
if (cygwait (evt ?: get_handle ()) == WAIT_OBJECT_0) waitret = cygwait (evt ?: get_handle (), cw_infinite,
cw_cancel | cw_sig_eintr);
if (waitret == WAIT_OBJECT_0)
status = io.Status; status = io.Status;
} }
if (evt) if (evt)
NtClose (evt); NtClose (evt);
if (status == STATUS_PIPE_LISTENING) if (waitret == WAIT_CANCELED)
pthread::static_cancel_self ();
else if (waitret == WAIT_SIGNALED)
set_errno (EINTR);
else if (status == STATUS_PIPE_LISTENING)
set_errno (EAGAIN); set_errno (EAGAIN);
else if (status != STATUS_PIPE_CONNECTED) else if (status != STATUS_PIPE_CONNECTED)
__seterrno_from_nt_status (status); __seterrno_from_nt_status (status);
@ -929,7 +936,9 @@ fhandler_socket_unix::disconnect_pipe (HANDLE ph)
status = NtFsControlFile (ph, NULL, NULL, NULL, &io, FSCTL_PIPE_DISCONNECT, status = NtFsControlFile (ph, NULL, NULL, NULL, &io, FSCTL_PIPE_DISCONNECT,
NULL, 0, NULL, 0); NULL, 0, NULL, 0);
if (status == STATUS_PENDING && cygwait (ph) == WAIT_OBJECT_0) /* Short-lived. Don't use cygwait. We don't want to be interrupted. */
if (status == STATUS_PENDING
&& WaitForSingleObject (ph, INFINITE) == WAIT_OBJECT_0)
status = io.Status; status = io.Status;
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
{ {
@ -1290,13 +1299,17 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
/* Our handle is now connected with a client. This handle is used /* Our handle is now connected with a client. This handle is used
for the accepted socket. Our handle has to be replaced with a for the accepted socket. Our handle has to be replaced with a
new instance handle for the next accept. */ new instance handle for the next accept. */
AcquireSRWLockExclusive (&io_lock);
HANDLE accepted = get_handle (); HANDLE accepted = get_handle ();
HANDLE new_inst = create_pipe_instance (); HANDLE new_inst = create_pipe_instance ();
int error = ENOBUFS; int error = ENOBUFS;
if (new_inst) if (!new_inst)
ReleaseSRWLockExclusive (&io_lock);
else
{ {
/* Set new io handle. */ /* Set new io handle. */
set_io_handle (new_inst); set_io_handle (new_inst);
ReleaseSRWLockExclusive (&io_lock);
/* Prepare new file descriptor. */ /* Prepare new file descriptor. */
cygheap_fdnew fd; cygheap_fdnew fd;
@ -1322,13 +1335,16 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
sock->set_sun_path (get_sun_path ()); sock->set_sun_path (get_sun_path ());
error = sock->recv_peer_name (); error = sock->recv_peer_name ();
if (error == 0) if (error == 0)
{
__try
{ {
if (peer) if (peer)
{ {
sun_name_t *sun = sock->get_peer_sun_path (); sun_name_t *sun = sock->get_peer_sun_path ();
if (sun) if (sun)
{ {
memcpy (peer, &sun->un, MIN (*len, sun->un_len)); memcpy (peer, &sun->un,
MIN (*len, sun->un_len));
*len = sun->un_len; *len = sun->un_len;
} }
else if (len) else if (len)
@ -1339,6 +1355,12 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
set_std_handle (fd); set_std_handle (fd);
return fd; return fd;
} }
__except (NO_ERROR)
{
error = EFAULT;
}
__endtry
}
delete sock; delete sock;
} }
fd.release (); fd.release ();