From 2d75c9687464472253e225b660471e3afecc958c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20K=C4=99pie=C5=84?= Date: Mon, 18 Dec 2023 11:07:04 +0100 Subject: [PATCH] Prevent an infinite loop in shutdown_listener() 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 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c index d951073a7e..ae69d22ee0 100644 --- a/bin/named/controlconf.c +++ b/bin/named/controlconf.c @@ -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); }