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.
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);
}