Commit c4cb365e authored by Prashant Varanasi's avatar Prashant Varanasi Committed by Mikio Hara

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/19758Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Reviewed-by: default avatarMikio Hara <mikioh.mikioh@gmail.com>
parent d1cc7f70
...@@ -120,7 +120,13 @@ func (fd *netFD) setWriteDeadline(t time.Time) error { ...@@ -120,7 +120,13 @@ func (fd *netFD) setWriteDeadline(t time.Time) error {
} }
func setDeadlineImpl(fd *netFD, t time.Time, mode int) 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() { if t.IsZero() {
d = 0 d = 0
} }
......
...@@ -26,6 +26,8 @@ var dialTimeoutTests = []struct { ...@@ -26,6 +26,8 @@ var dialTimeoutTests = []struct {
{-5 * time.Second, 0, -5 * time.Second, 100 * time.Millisecond}, {-5 * time.Second, 0, -5 * time.Second, 100 * time.Millisecond},
{0, -5 * time.Second, -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 {-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}, {50 * time.Millisecond, 0, 100 * time.Millisecond, time.Second},
{0, 50 * time.Millisecond, 100 * time.Millisecond, time.Second}, {0, 50 * time.Millisecond, 100 * time.Millisecond, time.Second},
...@@ -99,6 +101,54 @@ func TestDialTimeout(t *testing.T) { ...@@ -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 { var acceptTimeoutTests = []struct {
timeout time.Duration timeout time.Duration
xerrs [2]error // expected errors in transition xerrs [2]error // expected errors in transition
......
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