[release-branch.go1.3] net: prevent spurious on-connect events via epoll on linux

««« CL 120820043 / 06a4b59c1393
net: prevent spurious on-connect events via epoll on linux

On Linux, adding a socket descriptor to epoll instance before getting
the EINPROGRESS return value from connect system call could be a root
cause of spurious on-connect events.

See golang.org/issue/8276, golang.org/issue/8426 for further information.

All credit to Jason Eggleston <jason@eggnet.com>

Fixes #8276.
Fixes #8426.

LGTM=dvyukov
R=dvyukov, golang-codereviews, adg, dave, iant, alex.brainman
CC=golang-codereviews
https://golang.org/cl/120820043
»»»

TBR=r, rsc
CC=golang-codereviews
https://golang.org/cl/128110045
diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go
index b82ecd1..e22861a 100644
--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -68,16 +68,19 @@
 	return fd.net + ":" + ls + "->" + rs
 }
 
-func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
+func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
 	// Do not need to call fd.writeLock here,
 	// because fd is not yet accessible to user,
 	// so no concurrent operations are possible.
-	if err := fd.pd.PrepareWrite(); err != nil {
-		return err
-	}
 	switch err := syscall.Connect(fd.sysfd, ra); err {
 	case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
 	case nil, syscall.EISCONN:
+		if !deadline.IsZero() && deadline.Before(time.Now()) {
+			return errTimeout
+		}
+		if err := fd.init(); err != nil {
+			return err
+		}
 		return nil
 	case syscall.EINVAL:
 		// On Solaris we can see EINVAL if the socket has
@@ -92,6 +95,13 @@
 	default:
 		return err
 	}
+	if err := fd.init(); err != nil {
+		return err
+	}
+	if !deadline.IsZero() {
+		fd.setWriteDeadline(deadline)
+		defer fd.setWriteDeadline(noDeadline)
+	}
 	for {
 		// Performing multiple connect system calls on a
 		// non-blocking socket under Unix variants does not
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index a1f6bc5..d1129dc 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -313,10 +313,17 @@
 	runtime.SetFinalizer(fd, (*netFD).Close)
 }
 
-func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
+func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
 	// Do not need to call fd.writeLock here,
 	// because fd is not yet accessible to user,
 	// so no concurrent operations are possible.
+	if err := fd.init(); err != nil {
+		return err
+	}
+	if !deadline.IsZero() {
+		fd.setWriteDeadline(deadline)
+		defer fd.setWriteDeadline(noDeadline)
+	}
 	if !canUseConnectEx(fd.net) {
 		return syscall.Connect(fd.sysfd, ra)
 	}
diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go
index a6ef874..c80c7d6 100644
--- a/src/pkg/net/sock_posix.go
+++ b/src/pkg/net/sock_posix.go
@@ -107,24 +107,18 @@
 			}
 		}
 	}
-	if err := fd.init(); err != nil {
-		return err
-	}
 	var rsa syscall.Sockaddr
 	if raddr != nil {
 		if rsa, err = raddr.sockaddr(fd.family); err != nil {
 			return err
-		} else if rsa != nil {
-			if !deadline.IsZero() {
-				fd.setWriteDeadline(deadline)
-			}
-			if err := fd.connect(lsa, rsa); err != nil {
-				return err
-			}
-			fd.isConnected = true
-			if !deadline.IsZero() {
-				fd.setWriteDeadline(noDeadline)
-			}
+		}
+		if err := fd.connect(lsa, rsa, deadline); err != nil {
+			return err
+		}
+		fd.isConnected = true
+	} else {
+		if err := fd.init(); err != nil {
+			return err
 		}
 	}
 	lsa, _ = syscall.Getsockname(fd.sysfd)