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 <snyh@snyh.org>
PiperOrigin-RevId: 598940274
This commit is contained in:
Ayush Ranjan
2024-01-16 13:32:33 -08:00
committed by gVisor bot
co-authored by snyh
parent be48200c0e
commit f1f3dbd6c2
3 changed files with 25 additions and 0 deletions
+1
View File
@@ -25,6 +25,7 @@ go_test(
srcs = ["gonet_test.go"],
library = ":gonet",
deps = [
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/header",
"//pkg/tcpip/link/loopback",
+1
View File
@@ -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
}
+23
View File
@@ -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()
}