From 46e6577014c849d7306c63905db25f3c695fa7e7 Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Thu, 27 Dec 2018 14:58:41 -0800 Subject: Fix deadlock between epoll_wait and getdents epoll_wait acquires EventPoll.listsMu (in EventPoll.ReadEvents) and then calls Inotify.Readiness which tries to acquire Inotify.evMu. getdents acquires Inotify.evMu (in Inotify.queueEvent) and then calls readyCallback.Callback which tries to acquire EventPoll.listsMu. The fix is to release Inotify.evMu before calling Queue.Notify. Queue is thread-safe and doesn't require Inotify.evMu to be held. Closes #121 PiperOrigin-RevId: 227066695 Change-Id: Id29364bb940d1727f33a5dff9a3c52f390c15761 --- pkg/sentry/fs/inotify.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'pkg/sentry') diff --git a/pkg/sentry/fs/inotify.go b/pkg/sentry/fs/inotify.go index f251df0d1..51ece5ed0 100644 --- a/pkg/sentry/fs/inotify.go +++ b/pkg/sentry/fs/inotify.go @@ -42,14 +42,14 @@ type Inotify struct { // user, since we may aggressively reuse an id on S/R. id uint64 - // evMu *only* protects the event queue. We need a separate lock because + waiter.Queue `state:"nosave"` + + // evMu *only* protects the events list. We need a separate lock because // while queuing events, a watch needs to lock the event queue, and using mu // for that would violate lock ordering since at that point the calling // goroutine already holds Watch.target.Watches.mu. evMu sync.Mutex `state:"nosave"` - waiter.Queue `state:"nosave"` - // A list of pending events for this inotify instance. Protected by evMu. events ilist.List @@ -212,7 +212,6 @@ func (i *Inotify) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArg func (i *Inotify) queueEvent(ev *Event) { i.evMu.Lock() - defer i.evMu.Unlock() // Check if we should coalesce the event we're about to queue with the last // one currently in the queue. Events are coalesced if they are identical. @@ -221,11 +220,17 @@ func (i *Inotify) queueEvent(ev *Event) { // "Coalesce" the two events by simply not queuing the new one. We // don't need to raise a waiter.EventIn notification because no new // data is available for reading. + i.evMu.Unlock() return } } i.events.PushBack(ev) + + // Release mutex before notifying waiters because we don't control what they + // can do. + i.evMu.Unlock() + i.Queue.Notify(waiter.EventIn) } -- cgit v1.2.3