Skip to content

Commit 5d85724

Browse files
nixprimegvisor-bot
authored andcommitted
vfs: clear temporary notReady lists
`EpollInstance.Readiness()` and `EpollInstance.ReadEvents()` queue not-ready `epollInterest`s on temporary lists named `notReady`, so that they can be rechecked (with `EpollInstance.readyMu` locked) for concurrent `epollInterest.NotifyEvents()` before returning. When `epollInterest`s have *not* been concurrently notified, they are left on the `notReady` list, which is discarded when the containing function returns. However, since `notReady` is an intrusive linked list, this means that not-ready `epollInterest`s can retain pointers to other not-ready `epollInterest`s in `epollInterest.epollInterestEntry.Prev/Next`. If a not-ready `epollInterest` is deleted (because e.g. its corresponding `FileDescription` reaches zero references), but another live `epollInterest` has a pointer to the deleted `epollInterest`, then the deleted `epollInterest` will be preserved by save/restore; after restore, `epollInterest.afterLoad()` will cause the deleted `epollInterest` to be marked ready again, causing it to be incorrectly polled by the first post-restore call to `EpollInstance.Readiness()` or `EpollInstance.ReadEvents()` on the affected `EpollInstance`. Fix this by unconditionally removing `epollInterest`s from `notReady` lists. As an additional defensive measure, skip `epollInterest.afterLoad()` for deleted `epollInterest`s. PiperOrigin-RevId: 892621271
1 parent a65eb6a commit 5d85724

2 files changed

Lines changed: 10 additions & 5 deletions

File tree

pkg/sentry/vfs/epoll.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ type epollInterest struct {
9797
waiter waiter.Entry
9898

9999
// mask is the event mask associated with this registration, including
100-
// flags EPOLLET and EPOLLONESHOT. mask is protected by epoll.interestMu.
100+
// flags EPOLLET and EPOLLONESHOT. If mask is 0, the epollInterest has been
101+
// deleted. mask is protected by epoll.interestMu.
101102
mask uint32
102103

103104
// ready is true if epollInterestEntry is linked into epoll.ready. readySeq
@@ -137,10 +138,11 @@ func (ep *EpollInstance) Release(ctx context.Context) {
137138
defer ep.interestMu.Unlock()
138139
for key, epi := range ep.interest {
139140
file := key.file
141+
file.EventUnregister(&epi.waiter)
142+
epi.mask = 0
140143
file.epollMu.Lock()
141144
delete(file.epolls, epi)
142145
file.epollMu.Unlock()
143-
file.EventUnregister(&epi.waiter)
144146
}
145147
ep.interest = nil
146148
}
@@ -177,9 +179,9 @@ func (ep *EpollInstance) Readiness(mask waiter.EventMask) waiter.EventMask {
177179
var next *epollInterest
178180
for epi := notReady.Front(); epi != nil; epi = next {
179181
next = epi.Next()
182+
notReady.Remove(epi)
180183
if epi.readySeq == ep.readySeq {
181184
// epi.NotifyEvent() was called while we were running.
182-
notReady.Remove(epi)
183185
ep.ready.PushBack(epi)
184186
notify = true
185187
} else {
@@ -402,6 +404,7 @@ func (epi *epollInterest) NotifyEvent(waiter.EventMask) {
402404
// Preconditions: ep.interestMu must be locked.
403405
func (ep *EpollInstance) removeLocked(epi *epollInterest) {
404406
delete(ep.interest, epi.key)
407+
epi.mask = 0
405408
ep.readyMu.Lock()
406409
if epi.ready {
407410
epi.ready = false
@@ -443,9 +446,9 @@ func (ep *EpollInstance) ReadEvents(events []linux.EpollEvent, maxEvents int) []
443446
var next *epollInterest
444447
for epi := notReady.Front(); epi != nil; epi = next {
445448
next = epi.Next()
449+
notReady.Remove(epi)
446450
if epi.readySeq == ep.readySeq {
447451
// epi.NotifyEvent() was called while we were running.
448-
notReady.Remove(epi)
449452
ep.ready.PushBack(epi)
450453
notify = true
451454
} else {

pkg/sentry/vfs/save_restore.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,5 +185,7 @@ func (mnt *Mount) afterLoad(goContext.Context) {
185185
func (epi *epollInterest) afterLoad(goContext.Context) {
186186
// Mark all epollInterests as ready after restore so that the next call to
187187
// EpollInstance.ReadEvents() rechecks their readiness.
188-
epi.waiter.NotifyEvent(waiter.EventMaskFromLinux(epi.mask))
188+
if epi.mask != 0 {
189+
epi.waiter.NotifyEvent(waiter.EventMaskFromLinux(epi.mask))
190+
}
189191
}

0 commit comments

Comments
 (0)