Commit 7719016e authored by Sergey Zagursky's avatar Sergey Zagursky Committed by Ian Lance Taylor

net: fix improper Context.Deadline usage in DialContext

The existing implementation is erroneously assume that having no
deadline in context.Context means that time returned from Deadline
method will have IsZero() == true. But technically speaking this is an
invalid assumption. The context.Context interface specification doesn't
specify what time should be returned from Deadline method when there is
no deadline set. It only specifies that second result of Deadline should
be false.

Fixes #35594

Change-Id: Ife00aad77ab3585e469f15017550ac6c0431b140
Reviewed-on: https://go-review.googlesource.com/c/go/+/207297
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent 4de3c7d3
......@@ -529,20 +529,21 @@ func (sd *sysDialer) dialSerial(ctx context.Context, ras addrList) (Conn, error)
default:
}
deadline, _ := ctx.Deadline()
partialDeadline, err := partialDeadline(time.Now(), deadline, len(ras)-i)
if err != nil {
// Ran out of time.
if firstErr == nil {
firstErr = &OpError{Op: "dial", Net: sd.network, Source: sd.LocalAddr, Addr: ra, Err: err}
}
break
}
dialCtx := ctx
if partialDeadline.Before(deadline) {
var cancel context.CancelFunc
dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
defer cancel()
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
partialDeadline, err := partialDeadline(time.Now(), deadline, len(ras)-i)
if err != nil {
// Ran out of time.
if firstErr == nil {
firstErr = &OpError{Op: "dial", Net: sd.network, Source: sd.LocalAddr, Addr: ra, Err: err}
}
break
}
if partialDeadline.Before(deadline) {
var cancel context.CancelFunc
dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
defer cancel()
}
}
c, err := sd.dialSingle(dialCtx, ra)
......
......@@ -980,3 +980,32 @@ func mustHaveExternalNetwork(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
}
}
type contextWithNonZeroDeadline struct {
context.Context
}
func (contextWithNonZeroDeadline) Deadline() (time.Time, bool) {
// Return non-zero time.Time value with false indicating that no deadline is set.
return time.Unix(0, 0), false
}
func TestDialWithNonZeroDeadline(t *testing.T) {
ln, err := newLocalListener("tcp")
if err != nil {
t.Fatal(err)
}
defer ln.Close()
_, port, err := SplitHostPort(ln.Addr().String())
if err != nil {
t.Fatal(err)
}
ctx := contextWithNonZeroDeadline{Context: context.Background()}
var dialer Dialer
c, err := dialer.DialContext(ctx, "tcp", JoinHostPort("", port))
if err != nil {
t.Fatal(err)
}
c.Close()
}
......@@ -96,7 +96,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc
if err := fd.pfd.Init(fd.net, true); err != nil {
return nil, err
}
if deadline, _ := ctx.Deadline(); !deadline.IsZero() {
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
fd.pfd.SetWriteDeadline(deadline)
defer fd.pfd.SetWriteDeadline(noDeadline)
}
......
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