Try to handle concurrent close on socket more gracefully

* fhandler_socket.cc (LOCK_EVENTS): Don't enter critical section with
	invalid mutex handle since then socket has been closed.
	(UNLOCK_EVENTS): Close critical section.
	(fhandler_socket::evaluate_events): Handle calling connect on shutdown
	socket.
	(fhandler_socket::wait_for_events): Try for pthread_testcancel in case
	of WAIT_FAILED.  Try to come up with a better errno in case we waited
	on an invalid handle.
	(fhandler_socket::release_events): Change wsock_mtx and wsock_evt to
	NULL under lock to avoid accessing invalid handle.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2015-06-15 18:29:56 +02:00
parent 6f9e2f3ed5
commit 79d65a1ed2
2 changed files with 53 additions and 14 deletions

View File

@ -1,3 +1,16 @@
2015-06-15 Corinna Vinschen <corinna@vinschen.de>
* fhandler_socket.cc (LOCK_EVENTS): Don't enter critical section with
invalid mutex handle since then socket has been closed.
(UNLOCK_EVENTS): Close critical section.
(fhandler_socket::evaluate_events): Handle calling connect on shutdown
socket.
(fhandler_socket::wait_for_events): Try for pthread_testcancel in case
of WAIT_FAILED. Try to come up with a better errno in case we waited
on an invalid handle.
(fhandler_socket::release_events): Change wsock_mtx and wsock_evt to
NULL under lock to avoid accessing invalid handle.
2015-06-15 Corinna Vinschen <corinna@vinschen.de> 2015-06-15 Corinna Vinschen <corinna@vinschen.de>
* net.cc (errmap): Handle more Winsock error codes. * net.cc (errmap): Handle more Winsock error codes.

View File

@ -501,8 +501,14 @@ fhandler_socket::af_local_set_secret (char *buf)
counted as one socket. */ counted as one socket. */
#define NUM_SOCKS (32768 / sizeof (wsa_event)) #define NUM_SOCKS (32768 / sizeof (wsa_event))
#define LOCK_EVENTS WaitForSingleObject (wsock_mtx, INFINITE) #define LOCK_EVENTS \
#define UNLOCK_EVENTS ReleaseMutex (wsock_mtx) if (wsock_mtx && \
WaitForSingleObject (wsock_mtx, INFINITE) != WAIT_FAILED) \
{
#define UNLOCK_EVENTS \
ReleaseMutex (wsock_mtx); \
}
static wsa_event wsa_events[NUM_SOCKS] __attribute__((section (".cygwin_dll_common"), shared)); static wsa_event wsa_events[NUM_SOCKS] __attribute__((section (".cygwin_dll_common"), shared));
@ -709,14 +715,21 @@ fhandler_socket::evaluate_events (const long event_mask, long &events,
wsock_events->events &= ~FD_CONNECT; wsock_events->events &= ~FD_CONNECT;
wsock_events->connect_errorcode = 0; wsock_events->connect_errorcode = 0;
} }
/* This test makes the accept function behave as on Linux when /* This test makes accept/connect behave as on Linux when accept/connect
accept is called on a socket for which shutdown for the read side is called on a socket for which shutdown has been called. The second
has been called. The second half of this code is in the shutdown half of this code is in the shutdown method. */
method. See there for more info. */ if (events & FD_CLOSE)
if ((event_mask & FD_ACCEPT) && (events & FD_CLOSE))
{ {
WSASetLastError (WSAEINVAL); if ((event_mask & FD_ACCEPT) && saw_shutdown_read ())
ret = SOCKET_ERROR; {
WSASetLastError (WSAEINVAL);
ret = SOCKET_ERROR;
}
if (event_mask & FD_CONNECT)
{
WSASetLastError (WSAECONNRESET);
ret = SOCKET_ERROR;
}
} }
if (erase) if (erase)
wsock_events->events &= ~(events & ~(FD_WRITE | FD_CLOSE)); wsock_events->events &= ~(events & ~(FD_WRITE | FD_CLOSE));
@ -750,7 +763,8 @@ fhandler_socket::wait_for_events (const long event_mask, const DWORD flags)
{ {
case WSA_WAIT_TIMEOUT: case WSA_WAIT_TIMEOUT:
pthread_testcancel (); pthread_testcancel ();
/*FALLTHRU*/ break;
case WSA_WAIT_EVENT_0: case WSA_WAIT_EVENT_0:
break; break;
@ -761,19 +775,31 @@ fhandler_socket::wait_for_events (const long event_mask, const DWORD flags)
return SOCKET_ERROR; return SOCKET_ERROR;
default: default:
WSASetLastError (WSAEFAULT); pthread_testcancel ();
/* wsock_evt can be NULL. We're generating the same errno values
as for sockets on which shutdown has been called. */
if (WSAGetLastError () != WSA_INVALID_HANDLE)
WSASetLastError (WSAEFAULT);
else
WSASetLastError ((event_mask & FD_CONNECT) ? WSAECONNRESET
: WSAEINVAL);
return SOCKET_ERROR; return SOCKET_ERROR;
} }
} }
return ret; return ret;
} }
void void
fhandler_socket::release_events () fhandler_socket::release_events ()
{ {
NtClose (wsock_evt); HANDLE evt = wsock_evt;
NtClose (wsock_mtx); HANDLE mtx = wsock_mtx;
LOCK_EVENTS;
wsock_evt = wsock_mtx = NULL;
} ReleaseMutex (mtx); /* == UNLOCK_EVENTS, but note using local mtx here. */
NtClose (evt);
NtClose (mtx);
} }
/* Called from net.cc:fdsock() if a freshly created socket is not /* Called from net.cc:fdsock() if a freshly created socket is not