Subject: Bug [336441ed59]: Buffer infoPtr between socket creation and insertion into info structure in thread local memory. Backported fix from commit [65b320b464] from branch "bug-[13d3af3ad5]".

From: oehhar <[email-masked]>
Date: Sat, 22 Mar 2014 16:14:26 +0000

---
 win/tclWinSock.c | 213 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 140 insertions(+), 73 deletions(-)

diff --git a/win/tclWinSock.c b/win/tclWinSock.c
index 9fa01c9..f0b210e 100644
--- a/win/tclWinSock.c
+++ b/win/tclWinSock.c
@@ -167,6 +167,10 @@ typedef struct {
 				 * socketThread has been initialized and has
 				 * started. */
     HANDLE socketListLock;	/* Win32 Event to lock the socketList */
+    SocketInfo *pendingSocketInfo;
+				/* This socket is opened but not jet in the
+				 * list. This value is also checked by
+				 * the event structure. */
     SocketInfo *socketList;	/* Every open socket in this thread has an
 				 * entry on this list. */
 } ThreadSpecificData;
@@ -329,6 +333,7 @@ InitSockets(void)
 
     if (tsdPtr == NULL) {
 	tsdPtr = TCL_TSD_INIT(&dataKey);
+	tsdPtr->pendingSocketInfo = NULL;
 	tsdPtr->socketList = NULL;
 	tsdPtr->hwnd       = NULL;
 	tsdPtr->threadId   = Tcl_GetCurrentThread();
@@ -923,12 +928,10 @@ CreateSocket(
 				 * asynchronously. */
 {
     u_long flag = 1;		/* Indicates nonblocking mode. */
-    int asyncConnect = 0;	/* Will be 1 if async connect is in
-				 * progress. */
     SOCKADDR_IN sockaddr;	/* Socket address */
     SOCKADDR_IN mysockaddr;	/* Socket address for client */
     SOCKET sock = INVALID_SOCKET;
-    SocketInfo *infoPtr;	/* The returned value. */
+    SocketInfo *infoPtr=NULL;	/* The returned value. */
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
 	    TclThreadDataKeyGet(&dataKey);
 
@@ -1007,6 +1010,15 @@ CreateSocket(
 	infoPtr->selectEvents = FD_ACCEPT;
 	infoPtr->watchEvents |= FD_ACCEPT;
 
+	/*
+	 * Register for interest in events in the select mask. Note that this
+	 * automatically places the socket into non-blocking mode.
+	 */
+    
+	ioctlsocket(sock, (long) FIONBIO, &flag);
+	SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+		(LPARAM) infoPtr);
+
     } else {
 	/*
 	 * Try to bind to a local port, if specified.
@@ -1020,14 +1032,48 @@ CreateSocket(
 	}
 
 	/*
+	 * Allocate socket info structure
+	 */
+
+	infoPtr = NewSocketInfo(sock);
+
+	/*
 	 * Set the socket into nonblocking mode if the connect should be done
-	 * in the background.
+	 * in the background. Activate connect notification.
 	 */
 
 	if (async) {
-	    if (ioctlsocket(sock, (long) FIONBIO, &flag) == SOCKET_ERROR) {
-		goto error;
-	    }
+
+	    /* get infoPtr lock */
+	    WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
+
+	    /*
+	     * Buffer new infoPtr in the tsd memory as long as it is not in
+	     * the info list. This allows the event procedure to process the
+	     * event.
+	     */
+
+	    tsdPtr->pendingSocketInfo = infoPtr;
+
+	    /*
+	     * Set connect mask to connect events
+	     * This is activated by a SOCKET_SELECT message to the notifier
+	     * thread.
+	     */
+
+	    infoPtr->selectEvents |= FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE;
+	    infoPtr->flags |= SOCKET_ASYNC_CONNECT;
+	    
+	    /*
+	     * Free list lock
+	     */
+	    SetEvent(tsdPtr->socketListLock);
+
+	    /* activate accept notification and put in async mode */
+	    ioctlsocket(sock, (long) FIONBIO, &flag);
+	    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+	            (LPARAM) infoPtr);
+
 	}
 
 	/*
@@ -1045,35 +1091,26 @@ CreateSocket(
 	     * The connection is progressing in the background.
 	     */
 
-	    asyncConnect = 1;
-	}
+	} else {
 
-	/*
-	 * Add this socket to the global list of sockets.
-	 */
+	    /*
+	     * Set up the select mask for read/write events. If the connect
+	     * attempt has not completed, include connect events.
+	     */
 
-	infoPtr = NewSocketInfo(sock);
+	    infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
 
-	/*
-	 * Set up the select mask for read/write events. If the connect
-	 * attempt has not completed, include connect events.
-	 */
+	    /*
+	     * Register for interest in events in the select mask. Note that this
+	     * automatically places the socket into non-blocking mode.
+	     */
 
-	infoPtr->selectEvents = FD_READ | FD_WRITE | FD_CLOSE;
-	if (asyncConnect) {
-	    infoPtr->flags |= SOCKET_ASYNC_CONNECT;
-	    infoPtr->selectEvents |= FD_CONNECT;
+	    ioctlsocket(sock, (long) FIONBIO, &flag);
+	    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT,
+		    (LPARAM) infoPtr);
 	}
     }
 
-    /*
-     * Register for interest in events in the select mask. Note that this
-     * automatically places the socket into non-blocking mode.
-     */
-
-    ioctlsocket(sock, (long) FIONBIO, &flag);
-    SendMessage(tsdPtr->hwnd, SOCKET_SELECT, (WPARAM) SELECT, (LPARAM) infoPtr);
-
     return infoPtr;
 
   error:
@@ -1082,7 +1119,17 @@ CreateSocket(
 	Tcl_AppendResult(interp, "couldn't open socket: ",
 		Tcl_PosixError(interp), NULL);
     }
-    if (sock != INVALID_SOCKET) {
+    /*
+     * Clear the tsd socket list pointer if we did not wait for
+     * the FD_CONNECT asyncroneously
+     */
+    tsdPtr->pendingSocketInfo = NULL;
+    if (infoPtr != NULL) {
+	/*
+	 * Free the allocated socket info structure and close the socket
+	 */
+	TcpCloseProc(infoPtr, interp);
+    } else if (sock != INVALID_SOCKET) {
 	closesocket(sock);
     }
     return NULL;
@@ -1482,7 +1529,7 @@ TcpAccept(
     SetHandleInformation((HANDLE) newSocket, HANDLE_FLAG_INHERIT, 0);
 
     /*
-     * Add this socket to the global list of sockets.
+     * Allocate socket info structure
      */
 
     newInfoPtr = NewSocketInfo(newSocket);
@@ -2248,6 +2295,7 @@ SocketProc(
     int event, error;
     SOCKET socket;
     SocketInfo *infoPtr;
+    int info_found = 0;
     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
 #ifdef _WIN64
 	    GetWindowLongPtr(hwnd, GWLP_USERDATA);
@@ -2293,58 +2341,72 @@ SocketProc(
 	for (infoPtr = tsdPtr->socketList; infoPtr != NULL;
 		infoPtr = infoPtr->nextPtr) {
 	    if (infoPtr->socket == socket) {
-		/*
-		 * Update the socket state.
-		 *
-		 * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event
-		 * happens, then clear the FD_ACCEPT count. Otherwise,
-		 * increment the count if the current event is an FD_ACCEPT.
-		 */
+		info_found = 1;
+		break;
+	    }
+	}
+	/*
+	 * Check if there is a pending info structure not jet in the
+	 * list
+	 */
+	if ( !info_found
+		&& tsdPtr->pendingSocketInfo != NULL
+		&& tsdPtr->pendingSocketInfo->socket ==socket ) {
+	    infoPtr = tsdPtr->pendingSocketInfo;
+	    info_found = 1;
+	}
+	if (info_found) {
 
-		if (event & FD_CLOSE) {
-		    infoPtr->acceptEventCount = 0;
-		    infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
-		} else if (event & FD_ACCEPT) {
-		    infoPtr->acceptEventCount++;
-		}
+	    /*
+	     * Update the socket state.
+	     *
+	     * A count of FD_ACCEPTS is stored, so if an FD_CLOSE event
+	     * happens, then clear the FD_ACCEPT count. Otherwise,
+	     * increment the count if the current event is an FD_ACCEPT.
+	     */
 
-		if (event & FD_CONNECT) {
-		    /*
-		     * The socket is now connected, clear the async connect
-		     * flag.
-		     */
+	    if (event & FD_CLOSE) {
+	        infoPtr->acceptEventCount = 0;
+	        infoPtr->readyEvents &= ~(FD_WRITE|FD_ACCEPT);
+	    } else if (event & FD_ACCEPT) {
+		infoPtr->acceptEventCount++;
+	    }
 
-		    infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
+	    if (event & FD_CONNECT) {
+		/*
+		 * The socket is now connected, clear the async connect
+		 * flag.
+		 */
 
-		    /*
-		     * Remember any error that occurred so we can report
-		     * connection failures.
-		     */
+		infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
+
+		/*
+		 * Remember any error that occurred so we can report
+		 * connection failures.
+		 */
 
-		    if (error != ERROR_SUCCESS) {
-			TclWinConvertWSAError((DWORD) error);
-			infoPtr->lastError = Tcl_GetErrno();
-		    }
+		if (error != ERROR_SUCCESS) {
+		    TclWinConvertWSAError((DWORD) error);
+		    infoPtr->lastError = Tcl_GetErrno();
 		}
+	    }
 
-		if (infoPtr->flags & SOCKET_ASYNC_CONNECT) {
-		    infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
-		    if (error != ERROR_SUCCESS) {
-			TclWinConvertWSAError((DWORD) error);
-			infoPtr->lastError = Tcl_GetErrno();
-		    }
-		    infoPtr->readyEvents |= FD_WRITE;
+	    if (infoPtr->flags & SOCKET_ASYNC_CONNECT) {
+		infoPtr->flags &= ~(SOCKET_ASYNC_CONNECT);
+		if (error != ERROR_SUCCESS) {
+		    TclWinConvertWSAError((DWORD) error);
+		    infoPtr->lastError = Tcl_GetErrno();
 		}
-		infoPtr->readyEvents |= event;
+		infoPtr->readyEvents |= FD_WRITE;
+	    }
+	    infoPtr->readyEvents |= event;
 
-		/*
-		 * Wake up the Main Thread.
-		 */
+	    /*
+	     * Wake up the Main Thread.
+	     */
 
-		SetEvent(tsdPtr->readyEvent);
-		Tcl_ThreadAlert(tsdPtr->threadId);
-		break;
-	    }
+	    SetEvent(tsdPtr->readyEvent);
+	    Tcl_ThreadAlert(tsdPtr->threadId);
 	}
 	SetEvent(tsdPtr->socketListLock);
 	break;
@@ -2580,6 +2642,11 @@ TcpThreadActionProc(
 	WaitForSingleObject(tsdPtr->socketListLock, INFINITE);
 	infoPtr->nextPtr = tsdPtr->socketList;
 	tsdPtr->socketList = infoPtr;
+	
+	if (infoPtr == tsdPtr->pendingSocketInfo) {
+	    tsdPtr->pendingSocketInfo = NULL;
+	}
+	
 	SetEvent(tsdPtr->socketListLock);
 
 	notifyCmd = SELECT;
-- 
cgit v0.12