Commit ac8dae8b authored by Joerg Bornemann's avatar Joerg Bornemann
Browse files

QLocalServer/Win: Fix race condition in listen().


Suppose a client connects while the QLocalServer is still in the loop
that calls addListener. The connection would SetEvent(eventHandle),
but every call to ConnectNamedPipe would ResetEvent(eventHandle).
Thus, the connection is never detected by the notifier on eventHandle.
  Callers of addListener must check the connection state of every
listener to make sure that no client connected while setting up
listeners.

Task-number: QTBUG-49254
Change-Id: Ia961927ea76973708e6e3f73510695eb5d6a0e4c
Reviewed-by: default avatarOswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Showing with 40 additions and 32 deletions
...@@ -192,6 +192,9 @@ bool QLocalServerPrivate::addListener() ...@@ -192,6 +192,9 @@ bool QLocalServerPrivate::addListener()
memset(&listener.overlapped, 0, sizeof(listener.overlapped)); memset(&listener.overlapped, 0, sizeof(listener.overlapped));
listener.overlapped.hEvent = eventHandle; listener.overlapped.hEvent = eventHandle;
// Beware! ConnectNamedPipe will reset the eventHandle to non-signaled.
// Callers of addListener must check all listeners for connections.
if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) { if (!ConnectNamedPipe(listener.handle, &listener.overlapped)) {
switch (GetLastError()) { switch (GetLastError()) {
case ERROR_IO_PENDING: case ERROR_IO_PENDING:
...@@ -199,7 +202,6 @@ bool QLocalServerPrivate::addListener() ...@@ -199,7 +202,6 @@ bool QLocalServerPrivate::addListener()
break; break;
case ERROR_PIPE_CONNECTED: case ERROR_PIPE_CONNECTED:
listener.connected = true; listener.connected = true;
SetEvent(eventHandle);
break; break;
default: default:
CloseHandle(listener.handle); CloseHandle(listener.handle);
...@@ -251,6 +253,8 @@ bool QLocalServerPrivate::listen(const QString &name) ...@@ -251,6 +253,8 @@ bool QLocalServerPrivate::listen(const QString &name)
for (int i = 0; i < SYSTEM_MAX_PENDING_SOCKETS; ++i) for (int i = 0; i < SYSTEM_MAX_PENDING_SOCKETS; ++i)
if (!addListener()) if (!addListener())
return false; return false;
_q_onNewConnection();
return true; return true;
} }
...@@ -264,37 +268,43 @@ void QLocalServerPrivate::_q_onNewConnection() ...@@ -264,37 +268,43 @@ void QLocalServerPrivate::_q_onNewConnection()
{ {
Q_Q(QLocalServer); Q_Q(QLocalServer);
DWORD dummy; DWORD dummy;
bool tryAgain;
// Reset first, otherwise we could reset an event which was asserted do {
// immediately after we checked the conn status. tryAgain = false;
ResetEvent(eventHandle);
// Reset first, otherwise we could reset an event which was asserted
// Testing shows that there is indeed absolutely no guarantee which listener gets // immediately after we checked the conn status.
// a client connection first, so there is no way around polling all of them. ResetEvent(eventHandle);
for (int i = 0; i < listeners.size(); ) {
HANDLE handle = listeners[i].handle; // Testing shows that there is indeed absolutely no guarantee which listener gets
if (listeners[i].connected // a client connection first, so there is no way around polling all of them.
|| GetOverlappedResult(handle, &listeners[i].overlapped, &dummy, FALSE)) for (int i = 0; i < listeners.size(); ) {
{ HANDLE handle = listeners[i].handle;
listeners.removeAt(i); if (listeners[i].connected
|| GetOverlappedResult(handle, &listeners[i].overlapped, &dummy, FALSE))
addListener(); {
listeners.removeAt(i);
if (pendingConnections.size() > maxPendingConnections)
connectionEventNotifier->setEnabled(false); addListener();
// Make this the last thing so connected slots can wreak the least havoc if (pendingConnections.size() > maxPendingConnections)
q->incomingConnection((quintptr)handle); connectionEventNotifier->setEnabled(false);
} else { else
if (GetLastError() != ERROR_IO_INCOMPLETE) { tryAgain = true;
q->close();
setError(QLatin1String("QLocalServerPrivate::_q_onNewConnection")); // Make this the last thing so connected slots can wreak the least havoc
return; q->incomingConnection((quintptr)handle);
} else {
if (GetLastError() != ERROR_IO_INCOMPLETE) {
q->close();
setError(QLatin1String("QLocalServerPrivate::_q_onNewConnection"));
return;
}
++i;
} }
++i;
} }
} } while (tryAgain);
} }
void QLocalServerPrivate::closeServer() void QLocalServerPrivate::closeServer()
......
[processConnection:1 client]
windows
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment