]> Cypherpunks repositories - gostls13.git/commitdiff
net: fix for DialTimeout errors with large timeout
authorPrashant Varanasi <prashant@prashantv.com>
Sat, 20 Feb 2016 17:43:15 +0000 (09:43 -0800)
committerMikio Hara <mikioh.mikioh@gmail.com>
Tue, 23 Feb 2016 22:48:04 +0000 (22:48 +0000)
The existing implementation converts the deadline time to an int64,
but does not handle overflow. If the calculated deadline is negative
but the user specified deadline is in the future, then we can assume
the calculation overflowed, and set the deadline to math.MaxInt64.

Fixes #14431

Change-Id: I54dbb4f02bc7ffb9cae8cf62e4e967e9c6541ec6
Reviewed-on: https://go-review.googlesource.com/19758
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Mikio Hara <mikioh.mikioh@gmail.com>
src/net/fd_poll_runtime.go
src/net/timeout_test.go

index 8522ccebfb364315c2d5f57213d67b1d697a0a5a..5897e3d68c50631fbe15d5f51f08a46b26184ee5 100644 (file)
@@ -120,7 +120,13 @@ func (fd *netFD) setWriteDeadline(t time.Time) error {
 }
 
 func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
-       d := runtimeNano() + int64(t.Sub(time.Now()))
+       diff := int64(t.Sub(time.Now()))
+       d := runtimeNano() + diff
+       if d <= 0 && diff > 0 {
+               // If the user has a deadline in the future, but the delay calculation
+               // overflows, then set the deadline to the maximum possible value.
+               d = 1<<63 - 1
+       }
        if t.IsZero() {
                d = 0
        }
index 98e3164fb9897a12fc022be72cc2c955bdb770c2..d80e478c7720ed7f80d0504ed41c26098f4f15d3 100644 (file)
@@ -26,6 +26,8 @@ var dialTimeoutTests = []struct {
        {-5 * time.Second, 0, -5 * time.Second, 100 * time.Millisecond},
        {0, -5 * time.Second, -5 * time.Second, 100 * time.Millisecond},
        {-5 * time.Second, 5 * time.Second, -5 * time.Second, 100 * time.Millisecond}, // timeout over deadline
+       {-1 << 63, 0, time.Second, 100 * time.Millisecond},
+       {0, -1 << 63, time.Second, 100 * time.Millisecond},
 
        {50 * time.Millisecond, 0, 100 * time.Millisecond, time.Second},
        {0, 50 * time.Millisecond, 100 * time.Millisecond, time.Second},
@@ -99,6 +101,54 @@ func TestDialTimeout(t *testing.T) {
        }
 }
 
+var dialTimeoutMaxDurationTests = []struct {
+       timeout time.Duration
+       delta   time.Duration // for deadline
+}{
+       // Large timeouts that will overflow an int64 unix nanos.
+       {1<<63 - 1, 0},
+       {0, 1<<63 - 1},
+}
+
+func TestDialTimeoutMaxDuration(t *testing.T) {
+       t.Parallel()
+
+       ln, err := newLocalListener("tcp")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer ln.Close()
+
+       for i, tt := range dialTimeoutMaxDurationTests {
+               ch := make(chan error)
+               max := time.NewTimer(100 * time.Millisecond)
+               defer max.Stop()
+               go func() {
+                       d := Dialer{Timeout: tt.timeout}
+                       if tt.delta != 0 {
+                               d.Deadline = time.Now().Add(tt.delta)
+                       }
+                       c, err := d.Dial(ln.Addr().Network(), ln.Addr().String())
+                       if err == nil {
+                               c.Close()
+                       }
+                       ch <- err
+               }()
+
+               select {
+               case <-max.C:
+                       t.Fatalf("#%d: Dial didn't return in an expected time", i)
+               case err := <-ch:
+                       if perr := parseDialError(err); perr != nil {
+                               t.Error(perr)
+                       }
+                       if err != nil {
+                               t.Errorf("#%d: %v", i, err)
+                       }
+               }
+       }
+}
+
 var acceptTimeoutTests = []struct {
        timeout time.Duration
        xerrs   [2]error // expected errors in transition