[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)