]> Cypherpunks repositories - gostls13.git/commitdiff
net: fix improper Context.Deadline usage in DialContext
authorSergey Zagursky <gvozdoder@gmail.com>
Thu, 14 Nov 2019 20:07:01 +0000 (23:07 +0300)
committerIan Lance Taylor <iant@golang.org>
Fri, 15 Nov 2019 00:03:57 +0000 (00:03 +0000)
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: Ian Lance Taylor <iant@golang.org>
src/net/dial.go
src/net/dial_test.go
src/net/fd_unix.go

index 60ab0f2973cc9e69c1abd7d352aa8f489d678aa6..d8be1c222d042f70b03bfe726a70f644da2c5d8a 100644 (file)
@@ -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)
index 2eddac8284b50bb97eac6e50ae33621362fa5c42..4312a6df7143a2d2ef45cf18e3eae7937d57c3ca 100644 (file)
@@ -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()
+}
index a6d64538102154929da369b1147c8cb1b68ca5f5..da88c74f6b4baec98227954ccb28e69987bc90ad 100644 (file)
@@ -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)
        }