[socket] Fix race on SocketAsyncResult (#4389)
authorLudovic Henry <ludovic@xamarin.com>
Thu, 16 Feb 2017 01:46:26 +0000 (20:46 -0500)
committerGitHub <noreply@github.com>
Thu, 16 Feb 2017 01:46:26 +0000 (20:46 -0500)
It might be possible that the callback modifies the current SocketAsyncResult via the Init method. We observe it with AcceptAsync with a ReceiveAsync in the callback.

mcs/class/System/System.Net.Sockets/SocketAsyncResult.cs

index c63b1781660d77a48c9dd8b8f77dc1aae07f7a8a..2533129e7e5682876c582442c9f229ca7386838a 100644 (file)
@@ -145,22 +145,33 @@ namespace System.Net.Sockets
 
                        IsCompleted = true;
 
+                       /* It is possible that this.socket is modified by this.Init which has been called by the callback. This
+                        * would lead to inconsistency, as we would for example not release the correct socket.ReadSem or
+                        * socket.WriteSem.
+                        * For example, this can happen with AcceptAsync followed by a ReceiveAsync on the same
+                        * SocketAsyncEventArgs */
+                       Socket completedSocket = socket;
+                       SocketOperation completedOperation = operation;
+
                        AsyncCallback callback = AsyncCallback;
                        if (callback != null) {
                                ThreadPool.UnsafeQueueUserWorkItem (_ => callback (this), null);
                        }
 
-                       switch (operation) {
+                       /* Warning: any field on the current SocketAsyncResult might have changed, as the callback might have
+                        * called this.Init */
+
+                       switch (completedOperation) {
                        case SocketOperation.Receive:
                        case SocketOperation.ReceiveFrom:
                        case SocketOperation.ReceiveGeneric:
                        case SocketOperation.Accept:
-                               socket.ReadSem.Release ();
+                               completedSocket.ReadSem.Release ();
                                break;
                        case SocketOperation.Send:
                        case SocketOperation.SendTo:
                        case SocketOperation.SendGeneric:
-                               socket.WriteSem.Release ();
+                               completedSocket.WriteSem.Release ();
                                break;
                        }