]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Cancel all fetch events in dns_resolver_cancelfetch()
authorAram Sargsyan <aram@isc.org>
Mon, 14 Nov 2022 12:18:06 +0000 (12:18 +0000)
committerMichał Kępień <michal@isc.org>
Thu, 12 Jan 2023 11:43:32 +0000 (12:43 +0100)
Although 'dns_fetch_t' fetch can have two associated events, one for
each of 'DNS_EVENT_FETCHDONE' and 'DNS_EVENT_TRYSTALE' types, the
dns_resolver_cancelfetch() function is designed in a way that it
expects only one existing event, which it must cancel, and when it
happens so that 'stale-answer-client-timeout' is enabled and there
are two events, only one of them is canceled, and it results in an
assertion in dns_resolver_destroyfetch(), when it finds a dangling
event.

Change the logic of dns_resolver_cancelfetch() function so that it
cancels both the events (if they exist), and in the right order.

lib/dns/resolver.c
lib/ns/query.c

index 973337f2ac9f8ddc07c43264d7d7df825921fd72..411b0af6d4970c57d72b38df52ef9ef7e0513489 100644 (file)
@@ -10701,6 +10701,8 @@ fail:
 void
 dns_resolver_cancelfetch(dns_fetch_t *fetch) {
        fetchctx_t *fctx = NULL;
+       dns_fetchevent_t *event_trystale = NULL;
+       dns_fetchevent_t *event_fetchdone = NULL;
 
        REQUIRE(DNS_FETCH_VALID(fetch));
        fctx = fetch->private;
@@ -10711,24 +10713,69 @@ dns_resolver_cancelfetch(dns_fetch_t *fetch) {
        LOCK(&fctx->lock);
 
        /*
-        * Find the completion event for this fetch (as opposed
-        * to those for other fetches that have joined the same
-        * fctx) and send it with result = ISC_R_CANCELED.
+        * Find all the events associated with the provided 'fetch' (as opposed
+        * to those for other fetches that have joined the same fctx).  The
+        * event(s) found are only sent and removed from the fctx->events list
+        * after this loop is finished (i.e. the fctx->events list is not
+        * modified during iteration).
         */
        for (dns_fetchevent_t *event = ISC_LIST_HEAD(fctx->events);
             event != NULL; event = ISC_LIST_NEXT(event, ev_link))
        {
-               if (event->fetch == fetch) {
-                       isc_task_t *task = event->ev_sender;
-                       ISC_LIST_UNLINK(fctx->events, event, ev_link);
-                       event->ev_sender = fctx;
-                       event->result = ISC_R_CANCELED;
+               /*
+                * Only process events associated with the provided 'fetch'.
+                */
+               if (event->fetch != fetch) {
+                       continue;
+               }
+
+               /*
+                * Track various events associated with the provided 'fetch' in
+                * separate variables as they will need to be sent in a
+                * specific order.
+                */
+               switch (event->ev_type) {
+               case DNS_EVENT_TRYSTALE:
+                       INSIST(event_trystale == NULL);
+                       event_trystale = event;
+                       break;
+               case DNS_EVENT_FETCHDONE:
+                       INSIST(event_fetchdone == NULL);
+                       event_fetchdone = event;
+                       break;
+               default:
+                       UNREACHABLE();
+               }
 
-                       isc_task_sendanddetach(&task, ISC_EVENT_PTR(&event));
+               /*
+                * Break out of the loop once all possible types of events
+                * associated with the provided 'fetch' are found.
+                */
+               if (event_trystale != NULL && event_fetchdone != NULL) {
                        break;
                }
        }
 
+       /*
+        * The "trystale" event must be sent before the "fetchdone" event,
+        * because the latter clears the "recursing" query attribute, which is
+        * required by both events (handled by the same callback function).
+        */
+       if (event_trystale != NULL) {
+               isc_task_t *etask = event_trystale->ev_sender;
+               ISC_LIST_UNLINK(fctx->events, event_trystale, ev_link);
+               event_trystale->ev_sender = fctx;
+               event_trystale->result = ISC_R_CANCELED;
+               isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event_trystale));
+       }
+       if (event_fetchdone != NULL) {
+               isc_task_t *etask = event_fetchdone->ev_sender;
+               ISC_LIST_UNLINK(fctx->events, event_fetchdone, ev_link);
+               event_fetchdone->ev_sender = fctx;
+               event_fetchdone->result = ISC_R_CANCELED;
+               isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event_fetchdone));
+       }
+
        /*
         * The fctx continues running even if no fetches remain;
         * the answer is still cached.
index d2dcafe683c6e0dbc2ba4cec2efaf87fe5bbffcd..eddbcd98ac0b7f29366bac2113fe92aa4cd6d1da 100644 (file)
@@ -6257,7 +6257,9 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
        CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
 
        if (event->ev_type == DNS_EVENT_TRYSTALE) {
-               query_lookup_stale(client);
+               if (devent->result != ISC_R_CANCELED) {
+                       query_lookup_stale(client);
+               }
                isc_event_free(ISC_EVENT_PTR(&event));
                return;
        }