From f1f3dbd6c23491168cd04778f0fdacd57d2f8cfe Mon Sep 17 00:00:00 2001 From: Ayush Ranjan Date: Tue, 16 Jan 2024 13:29:45 -0800 Subject: [PATCH] Handle zero time correctly in gonet.deadlineTimer. After setDeadline(ch, timer, time.Time{}), the timer pointer is not reset and may point to an already expired timer. So the next call to setDeadline() will close the cancel channel, even though the timer did not expire. This was fixed by setting timer to nil if time.Time.IsZero(). Fixes #9885 Co-authored-by: snyh PiperOrigin-RevId: 598940274 --- pkg/tcpip/adapters/gonet/BUILD | 1 + pkg/tcpip/adapters/gonet/gonet.go | 1 + pkg/tcpip/adapters/gonet/gonet_test.go | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/pkg/tcpip/adapters/gonet/BUILD b/pkg/tcpip/adapters/gonet/BUILD index 626a2a544..8e53827a6 100644 --- a/pkg/tcpip/adapters/gonet/BUILD +++ b/pkg/tcpip/adapters/gonet/BUILD @@ -25,6 +25,7 @@ go_test( srcs = ["gonet_test.go"], library = ":gonet", deps = [ + "//pkg/sync", "//pkg/tcpip", "//pkg/tcpip/header", "//pkg/tcpip/link/loopback", diff --git a/pkg/tcpip/adapters/gonet/gonet.go b/pkg/tcpip/adapters/gonet/gonet.go index 7dab48cc6..9ad06ab27 100644 --- a/pkg/tcpip/adapters/gonet/gonet.go +++ b/pkg/tcpip/adapters/gonet/gonet.go @@ -179,6 +179,7 @@ func (d *deadlineTimer) setDeadline(cancelCh *chan struct{}, timer **time.Timer, // "A zero value for t means I/O operations will not time out." // - net.Conn.SetDeadline if t.IsZero() { + *timer = nil return } diff --git a/pkg/tcpip/adapters/gonet/gonet_test.go b/pkg/tcpip/adapters/gonet/gonet_test.go index c3ed4636d..d1e88a58e 100644 --- a/pkg/tcpip/adapters/gonet/gonet_test.go +++ b/pkg/tcpip/adapters/gonet/gonet_test.go @@ -25,6 +25,7 @@ import ( "time" "golang.org/x/net/nettest" + "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/link/loopback" @@ -1003,3 +1004,25 @@ func TestInterruptListender(t *testing.T) { func TestNetTest(t *testing.T) { nettest.TestConn(t, makePipe) } + +// NOTE(gvisor.dev/issue/9885): Regression test. +func TestDeadlineTimerAfterZeroValue(t *testing.T) { + timer := &deadlineTimer{} + timer.init() + + wg := sync.WaitGroup{} + ch := timer.readCancel() + wg.Add(1) + go func() { + defer wg.Done() + select { + case <-ch: + case <-time.After(1 * time.Second): + t.Fail() + } + }() + timer.SetReadDeadline(time.Now().Add(10 * time.Second)) + timer.SetReadDeadline(time.Time{}) + timer.SetReadDeadline(time.Unix(1, 0)) + wg.Wait() +}