]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Prevent an infinite loop in shutdown_listener()
authorMichał Kępień <michal@isc.org>
Mon, 18 Dec 2023 10:07:04 +0000 (11:07 +0100)
committerMichał Kępień <michal@isc.org>
Mon, 18 Dec 2023 10:07:04 +0000 (11:07 +0100)
The loop in shutdown_listener() assumes that the reference count for
every controlconnection_t object on the listener->connections linked
list will drop down to zero after the conn_shutdown() call in the loop's
body.  However, when the timing is just right, some netmgr callbacks for
a given control connection may still be awaiting processing by the same
event loop that executes shutdown_listener() when the latter is run.
Since these netmgr callbacks must be run in order for the reference
count for the relevant controlconnection_t objects to drop to zero, when
the scenario described above happens, shutdown_listener() runs into an
infinite loop due to one of the controlconnection_t objects on the
listener->connections linked list never going away from the head of that
list.

Fix by safely iterating through the listener->connections list and
initiating shutdown for all controlconnection_t objects found.  This
allows any pending netmgr callbacks to be run by the same event loop in
due course, i.e. after shutdown_listener() returns.

bin/named/controlconf.c

index d951073a7ec811b1195ed7871e7dcaf5fa3c30f8..ae69d22ee050f08c5fca4a6aa77f6567216b2464 100644 (file)
@@ -202,15 +202,22 @@ ISC_REFCOUNT_IMPL(controlconnection, conn_free);
 
 static void
 shutdown_listener(controllistener_t *listener) {
+       controlconnection_t *conn = NULL;
+       controlconnection_t *next = NULL;
+
        /* Don't shutdown the same listener twice */
        if (listener->shuttingdown) {
                return;
        }
        listener->shuttingdown = true;
 
-       for (controlconnection_t *conn = ISC_LIST_HEAD(listener->connections);
-            conn != NULL; conn = ISC_LIST_HEAD(listener->connections))
+       for (conn = ISC_LIST_HEAD(listener->connections); conn != NULL;
+            conn = next)
        {
+               /*
+                * 'conn' is likely to be freed by the conn_shutdown() call.
+                */
+               next = ISC_LIST_NEXT(conn, link);
                conn_shutdown(conn);
        }