From c4cb365ea23f4e3bd571f63a16d59ad6c01df145 Mon Sep 17 00:00:00 2001 From: Prashant Varanasi Date: Sat, 20 Feb 2016 09:43:15 -0800 Subject: [PATCH] net: fix for DialTimeout errors with large timeout 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 Reviewed-by: Mikio Hara --- src/net/fd_poll_runtime.go | 8 +++++- src/net/timeout_test.go | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/net/fd_poll_runtime.go b/src/net/fd_poll_runtime.go index 8522ccebfb..5897e3d68c 100644 --- a/src/net/fd_poll_runtime.go +++ b/src/net/fd_poll_runtime.go @@ -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 } diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index 98e3164fb9..d80e478c77 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -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 -- 2.48.1