Ensure that sentry/kernel/time.Timers do not tick after destruction.

Destroyed Timers may not be reachable by Kernel.pauseTimeLocked(), but may
still be reachable by Kernel.SaveTo() via pointers to dead Tasks /
ThreadGroups => ThreadGroup.itimerRealTimer.
PiperOrigin-RevId: 673590086
This commit is contained in:
Jamie Liu
2024-09-11 17:00:37 -07:00
committed by gVisor bot
parent cf4bb56fc6
commit e319328338
+31 -12
View File
@@ -434,8 +434,7 @@ type Timer struct {
// setting is the timer setting. setting is protected by mu.
setting Setting
// paused is true if the Timer is paused. paused is protected by mu.
paused bool
pauseState timerPauseState
// kicker is used to wake the Timer goroutine. The kicker pointer is
// immutable, but its state is protected by mu.
@@ -453,6 +452,20 @@ type Timer struct {
events chan struct{} `state:"nosave"`
}
type timerPauseState uint8
const (
// timerUnpaused indicates that the Timer is neither paused nor
// destroyed.
timerUnpaused timerPauseState = iota
// timerPaused indicates that the Timer is paused, not destroyed.
timerPaused
// timerDestroyed indicates that the Timer is destroyed.
timerDestroyed
)
// timerTickEvents are Clock events that require the Timer goroutine to Tick
// prematurely.
const timerTickEvents = ClockEventSet | ClockEventRateIncrease
@@ -488,13 +501,16 @@ func (t *Timer) init() {
go t.runGoroutine() // S/R-SAFE: synchronized by t.mu
}
// Destroy releases resources owned by the Timer. A Destroyed Timer must not be
// used again; in particular, a Destroyed Timer should not be Saved.
// Destroy releases resources owned by the Timer. Pause and Resume may be
// called on a Destroyed Timer and are no-ops. No other methods may be called
// on a Destroyed Timer.
func (t *Timer) Destroy() {
// Stop the Timer, ensuring that the Timer goroutine will not call
// t.kicker.Reset, before calling t.kicker.Stop.
t.mu.Lock()
t.setting.Enabled = false
// Set timerDestroyed to prevent t.Tick() from mutating Timer state.
t.pauseState = timerDestroyed
t.mu.Unlock()
t.kicker.Stop()
// Unregister t.entry, ensuring that the Clock will not send to t.events,
@@ -526,7 +542,7 @@ func (t *Timer) Tick() {
now := unlockedClock.Now()
t.mu.Lock()
defer t.mu.Unlock()
if t.paused {
if t.pauseState != timerUnpaused {
return
}
if t.clock != unlockedClock {
@@ -548,7 +564,10 @@ func (t *Timer) Tick() {
func (t *Timer) Pause() {
t.mu.Lock()
defer t.mu.Unlock()
t.paused = true
if t.pauseState != timerUnpaused {
return
}
t.pauseState = timerPaused
// t.kicker may be nil if we were restored but never resumed.
if t.kicker != nil {
t.kicker.Stop()
@@ -560,10 +579,10 @@ func (t *Timer) Pause() {
func (t *Timer) Resume() {
t.mu.Lock()
defer t.mu.Unlock()
if !t.paused {
if t.pauseState != timerPaused {
return
}
t.paused = false
t.pauseState = timerUnpaused
// Lazily initialize the Timer. We can't call Timer.init until Timer.Resume
// because save/restore will restore Timers before
@@ -589,8 +608,8 @@ func (t *Timer) Get() (Time, Setting) {
now := unlockedClock.Now()
t.mu.Lock()
defer t.mu.Unlock()
if t.paused {
panic(fmt.Sprintf("Timer.Get called on paused Timer %p", t))
if t.pauseState != timerUnpaused {
panic(fmt.Sprintf("Timer.Get called on Timer %p in pause state %v", t, t.pauseState))
}
if t.clock != unlockedClock {
now = t.clock.Now()
@@ -632,8 +651,8 @@ func (t *Timer) SwapAnd(s Setting, f func()) (Time, Setting) {
now := unlockedClock.Now()
t.mu.Lock()
defer t.mu.Unlock()
if t.paused {
panic(fmt.Sprintf("Timer.SwapAnd called on paused Timer %p", t))
if t.pauseState != timerUnpaused {
panic(fmt.Sprintf("Timer.SwapAnd called on Timer %p in pause state %v", t, t.pauseState))
}
if t.clock != unlockedClock {
now = t.clock.Now()