Commit 1657de2d authored by Andrew Gerrand's avatar Andrew Gerrand

[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
parent f36546bc
...@@ -68,16 +68,19 @@ func (fd *netFD) name() string { ...@@ -68,16 +68,19 @@ func (fd *netFD) name() string {
return fd.net + ":" + ls + "->" + rs 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, // Do not need to call fd.writeLock here,
// because fd is not yet accessible to user, // because fd is not yet accessible to user,
// so no concurrent operations are possible. // so no concurrent operations are possible.
if err := fd.pd.PrepareWrite(); err != nil {
return err
}
switch err := syscall.Connect(fd.sysfd, ra); err { switch err := syscall.Connect(fd.sysfd, ra); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR: case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case nil, syscall.EISCONN: case nil, syscall.EISCONN:
if !deadline.IsZero() && deadline.Before(time.Now()) {
return errTimeout
}
if err := fd.init(); err != nil {
return err
}
return nil return nil
case syscall.EINVAL: case syscall.EINVAL:
// On Solaris we can see EINVAL if the socket has // On Solaris we can see EINVAL if the socket has
...@@ -92,6 +95,13 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error { ...@@ -92,6 +95,13 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
default: default:
return err return err
} }
if err := fd.init(); err != nil {
return err
}
if !deadline.IsZero() {
fd.setWriteDeadline(deadline)
defer fd.setWriteDeadline(noDeadline)
}
for { for {
// Performing multiple connect system calls on a // Performing multiple connect system calls on a
// non-blocking socket under Unix variants does not // non-blocking socket under Unix variants does not
......
...@@ -313,10 +313,17 @@ func (fd *netFD) setAddr(laddr, raddr Addr) { ...@@ -313,10 +313,17 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
runtime.SetFinalizer(fd, (*netFD).Close) 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, // Do not need to call fd.writeLock here,
// because fd is not yet accessible to user, // because fd is not yet accessible to user,
// so no concurrent operations are possible. // 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) { if !canUseConnectEx(fd.net) {
return syscall.Connect(fd.sysfd, ra) return syscall.Connect(fd.sysfd, ra)
} }
......
...@@ -107,24 +107,18 @@ func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(sys ...@@ -107,24 +107,18 @@ func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(sys
} }
} }
} }
if err := fd.init(); err != nil {
return err
}
var rsa syscall.Sockaddr var rsa syscall.Sockaddr
if raddr != nil { if raddr != nil {
if rsa, err = raddr.sockaddr(fd.family); err != nil { if rsa, err = raddr.sockaddr(fd.family); err != nil {
return err return err
} else if rsa != nil { }
if !deadline.IsZero() { if err := fd.connect(lsa, rsa, deadline); err != nil {
fd.setWriteDeadline(deadline) return err
} }
if err := fd.connect(lsa, rsa); err != nil { fd.isConnected = true
return err } else {
} if err := fd.init(); err != nil {
fd.isConnected = true return err
if !deadline.IsZero() {
fd.setWriteDeadline(noDeadline)
}
} }
} }
lsa, _ = syscall.Getsockname(fd.sysfd) lsa, _ = syscall.Getsockname(fd.sysfd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment