aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/time.go
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2020-12-02 12:19:13 -0500
committerMichael Pratt <mpratt@google.com>2020-12-03 21:23:16 +0000
commitb78b427be5e4c8a51a2b01b39c1ce6c4f39a93dc (patch)
treebba69775729b570a1b49020c7a398ec508d151aa /src/runtime/time.go
parentb635e4b808bf45ebd66e9f687e18b9af6bd634c1 (diff)
downloadgo-b78b427be5e4c8a51a2b01b39c1ce6c4f39a93dc.tar.gz
go-b78b427be5e4c8a51a2b01b39c1ce6c4f39a93dc.zip
runtime, time: strictly enforce when, period constraints
timer.when must always be positive. addtimer and modtimer already check that it is non-negative; we expand it to include zero. Also upgrade from pinning bad values to throwing, as these values shouldn't be possible to pass (except as below). timeSleep may overflow timer.nextwhen. This would previously have been pinned by resetForSleep, now we fix it manually. runOneTimer may overflow timer.when when adding timer.period. Detect this and pin to maxWhen. addtimer is now too strict to allow TestOverflowRuntimeTimer to test an overflowed timer. Such a timer should not be possible; to help guard against accidental inclusion siftup / siftdown will check timers as it goes. This has been replaced with tests for period and sleep overflows. Change-Id: I17f9739e27ebcb20d87945c635050316fb8e9226 Reviewed-on: https://go-review.googlesource.com/c/go/+/274853 Trust: Michael Pratt <mpratt@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/runtime/time.go')
-rw-r--r--src/runtime/time.go31
1 files changed, 25 insertions, 6 deletions
diff --git a/src/runtime/time.go b/src/runtime/time.go
index 83d93c5686..d338705b7c 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -187,6 +187,9 @@ func timeSleep(ns int64) {
t.f = goroutineReady
t.arg = gp
t.nextwhen = nanotime() + ns
+ if t.nextwhen < 0 { // check for overflow.
+ t.nextwhen = maxWhen
+ }
gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1)
}
@@ -244,10 +247,14 @@ func goroutineReady(arg interface{}, seq uintptr) {
// That avoids the risk of changing the when field of a timer in some P's heap,
// which could cause the heap to become unsorted.
func addtimer(t *timer) {
- // when must never be negative; otherwise runtimer will overflow
- // during its delta calculation and never expire other runtime timers.
- if t.when < 0 {
- t.when = maxWhen
+ // when must be positive. A negative value will cause runtimer to
+ // overflow during its delta calculation and never expire other runtime
+ // timers. Zero will cause checkTimers to fail to notice the timer.
+ if t.when <= 0 {
+ throw("timer when must be positive")
+ }
+ if t.period < 0 {
+ throw("timer period must be non-negative")
}
if t.status != timerNoStatus {
throw("addtimer called with initialized timer")
@@ -408,8 +415,11 @@ func dodeltimer0(pp *p) {
// This is called by the netpoll code or time.Ticker.Reset or time.Timer.Reset.
// Reports whether the timer was modified before it was run.
func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) bool {
- if when < 0 {
- when = maxWhen
+ if when <= 0 {
+ throw("timer when must be positive")
+ }
+ if period < 0 {
+ throw("timer period must be non-negative")
}
status := uint32(timerNoStatus)
@@ -848,6 +858,9 @@ func runOneTimer(pp *p, t *timer, now int64) {
// Leave in heap but adjust next time to fire.
delta := t.when - now
t.when += t.period * (1 + -delta/t.period)
+ if t.when < 0 { // check for overflow.
+ t.when = maxWhen
+ }
siftdownTimer(pp.timers, 0)
if !atomic.Cas(&t.status, timerRunning, timerWaiting) {
badTimer()
@@ -1066,6 +1079,9 @@ func siftupTimer(t []*timer, i int) {
badTimer()
}
when := t[i].when
+ if when <= 0 {
+ badTimer()
+ }
tmp := t[i]
for i > 0 {
p := (i - 1) / 4 // parent
@@ -1086,6 +1102,9 @@ func siftdownTimer(t []*timer, i int) {
badTimer()
}
when := t[i].when
+ if when <= 0 {
+ badTimer()
+ }
tmp := t[i]
for {
c := i*4 + 1 // left child