]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix a possible deadlock in TCP accepting
authorWitold Kręcicki <wpk@isc.org>
Wed, 22 May 2019 10:37:03 +0000 (12:37 +0200)
committerWitold Krecicki <wpk@isc.org>
Fri, 24 May 2019 07:18:41 +0000 (03:18 -0400)
Each network thread holds an array of locks, indexed by a hash
of fd. When we accept a connection we hold a lock in accepting thread.
We then generate the thread number and lock bucket for the new
connection socket - if we hit the same thread and lock bucket as
accepting socket we get a deadlock. Avoid this by checking if we're
in the same thread/lock bucket and not locking in this case.

CHANGES
lib/isc/unix/socket.c

diff --git a/CHANGES b/CHANGES
index 53bf0b65f4074e908a010df5a37b04e660f9e328..335b9b2b02b3b18b140e9255772249a788c6a1dc 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+5238.  [bug]           Fix a possible deadlock in TCP code. [GL #1046]
+
 5237.  [bug]           Recurse to find the root server list with 'dig +trace'.
                        [GL #1028]
 
index d62af359d514c1c631d9483d1e05dadd2b2222a1..fe187028c232d0cf8d6ff9f563fbc9cbb2bcbee0 100644 (file)
@@ -2977,6 +2977,13 @@ internal_accept(isc__socket_t *sock) {
                NEWCONNSOCK(dev)->connected = 1;
                nthread = &manager->threads[NEWCONNSOCK(dev)->threadid];
 
+               /*
+                * We already hold a lock on one fdlock in accepting thread,
+                * we need to make sure that we don't double lock.
+                */
+               bool same_bucket = (sock->threadid == NEWCONNSOCK(dev)->threadid) &&
+                                  (FDLOCK_ID(sock->fd) == lockid);
+
                /*
                 * Use minimum mtu if possible.
                 */
@@ -2999,13 +3006,17 @@ internal_accept(isc__socket_t *sock) {
                        NEWCONNSOCK(dev)->active = 1;
                }
 
-               LOCK(&nthread->fdlock[lockid]);
+               if (!same_bucket) {
+                       LOCK(&nthread->fdlock[lockid]);
+               }
                nthread->fds[fd] = NEWCONNSOCK(dev);
                nthread->fdstate[fd] = MANAGED;
 #if defined(USE_EPOLL)
                nthread->epoll_events[fd] = 0;
 #endif
-               UNLOCK(&nthread->fdlock[lockid]);
+               if (!same_bucket) {
+                       UNLOCK(&nthread->fdlock[lockid]);
+               }
 
                LOCK(&manager->lock);