From 1e65eb5462b01c025f55069b7ead13eb64ef7e09 Mon Sep 17 00:00:00 2001
From: Nadav Har'El <nyh@cloudius-systems.com>
Date: Thu, 11 Jul 2013 16:18:43 +0300
Subject: [PATCH] Fix socket poll() deadlock

In commit 7ecbf29fd3965602b8a54436e3778d08934014a4 I added to the
poll_install() stage of poll() a check of the current state of the file -
to avoid the sleep if the file became ready before we managed to "install"
its poll request.

However, I wrongly believed it was necessary to put this check inside
the FD_LOCK together with the request installation. In fact, it doesn't
need to be in the same lock - all we need is for the check to happen
*after* the installation. The call to fo_poll() doesn't need to be in
the same FD_LOCK or even in an FD_LOCK at all.

Moreover, as it turns out, it must NOT be in an FD_LOCK() because this
results in a deadlock when polling sockets, caused by two different
code paths taking locks in opposite order:

1. Before this fix, poll() took FD_LOCK and called fo_poll() which
   called sopoll_generic() which took a SOCKBUF_LOCK

2. In the wake path, SOCKBUF_LOCK was taken, then so_wake_poll()
   is called which calls poll_wake() which takes FD_LOCK.
---
 core/poll.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/core/poll.c b/core/poll.c
index 62172bac1..daaa9a150 100644
--- a/core/poll.c
+++ b/core/poll.c
@@ -198,10 +198,9 @@ void poll_install(struct pollreq* p)
 
         FD_LOCK(fp);
         TAILQ_INSERT_TAIL(&fp->f_poll_list, pl, _link);
-        // We need to check for an existing event on this file here, while
-        // still holding the lock, so we don't lose an event between checking
-        // and installing. Note this means that poll_scan() in the beginning
-        // of poll() is redundant and may be removed in the future.
+        FD_UNLOCK(fp);
+        // We need to check if we missed an event on this file just before
+        // installing the poll request on it above.
         if(fo_poll(fp, entry->events)) {
             // Return immediately. poll() will call poll_scan() to get the
             // full list of events, and will call poll_uninstall() to undo
@@ -209,12 +208,9 @@ void poll_install(struct pollreq* p)
             mtx_lock(&p->_awake_mutex);
             p->_awake = true;
             mtx_unlock(&p->_awake_mutex);
-            FD_UNLOCK(fp);
             fdrop(fp);
             return;
         }
-        FD_UNLOCK(fp);
-
         fdrop(fp);
     }
 }
-- 
GitLab