/// <summary> /// Attempts to connect to the specified process. /// </summary> private NamedPipeClientStream TryConnectToProcess(int nodeProcessId, int timeout, long hostHandshake, long clientHandshake) { // Try and connect to the process. string pipeName = "MSBuild" + nodeProcessId; NamedPipeClientStream nodeStream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); CommunicationsUtilities.Trace("Attempting connect to PID {0} with pipe {1} with timeout {2} ms", nodeProcessId, pipeName, timeout); try { nodeStream.Connect(timeout); // Verify that the owner of the pipe is us. This prevents a security hole where a remote node has // been faked up with ACLs that would let us attach to it. It could then issue fake build requests back to // us, potentially causing us to execute builds that do harmful or unexpected things. The pipe owner can // only be set to the user's own SID by a normal, unprivileged process. The conditions where a faked up // remote node could set the owner to something else would also let it change owners on other objects, so // this would be a security flaw upstream of us. SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; PipeSecurity remoteSecurity = nodeStream.GetAccessControl(); IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); if (remoteOwner != identifier) { CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); throw new UnauthorizedAccessException(); } CommunicationsUtilities.Trace("Writing handshake to pipe {0}", pipeName); #if true nodeStream.WriteLongForHandshake(hostHandshake); #else // When the 4th and subsequent node start up, we see this taking a long period of time (0.5s or greater.) This is strictly for debugging purposes. DateTime writeStart = DateTime.UtcNow; nodeStream.WriteLong(HostHandshake); DateTime writeEnd = DateTime.UtcNow; Console.WriteLine("Node ProcessId {0} WriteLong {1}", nodeProcessId, (writeEnd - writeStart).TotalSeconds); #endif CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName); long handshake = nodeStream.ReadLongForHandshake(); if (handshake != clientHandshake) { CommunicationsUtilities.Trace("Handshake failed. Received {0} from client not {1}. Probably the client is a different MSBuild build.", handshake, clientHandshake); throw new InvalidOperationException(); } // We got a connection. CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", pipeName); return nodeStream; } catch (Exception e) { if (ExceptionHandling.IsCriticalException(e)) { throw; } // Can be: // UnauthorizedAccessException -- Couldn't connect, might not be a node. // IOException -- Couldn't connect, already in use. // TimeoutException -- Couldn't connect, might not be a node. // InvalidOperationException – Couldn’t connect, probably a different build CommunicationsUtilities.Trace("Failed to connect to pipe {0}. {1}", pipeName, e.Message.TrimEnd()); // If we don't close any stream, we might hang up the child if (nodeStream != null) { nodeStream.Close(); } } return null; }