bcb1e2
From 20424b3bfe8d3fae92c11a30e79aeffd26dc2891 Mon Sep 17 00:00:00 2001
bcb1e2
From: Aram Sargsyan <aram@isc.org>
bcb1e2
Date: Mon, 14 Nov 2022 12:18:06 +0000
bcb1e2
Subject: [PATCH] Cancel all fetch events in dns_resolver_cancelfetch()
bcb1e2
bcb1e2
Although 'dns_fetch_t' fetch can have two associated events, one for
bcb1e2
each of 'DNS_EVENT_FETCHDONE' and 'DNS_EVENT_TRYSTALE' types, the
bcb1e2
dns_resolver_cancelfetch() function is designed in a way that it
bcb1e2
expects only one existing event, which it must cancel, and when it
bcb1e2
happens so that 'stale-answer-client-timeout' is enabled and there
bcb1e2
are two events, only one of them is canceled, and it results in an
bcb1e2
assertion in dns_resolver_destroyfetch(), when it finds a dangling
bcb1e2
event.
bcb1e2
bcb1e2
Change the logic of dns_resolver_cancelfetch() function so that it
bcb1e2
cancels both the events (if they exist), and in the right order.
bcb1e2
bcb1e2
(cherry picked from commit ec2098ca35039e4f81fd0aa7c525eb960b8f47bf)
bcb1e2
---
bcb1e2
 lib/dns/resolver.c | 53 +++++++++++++++++++++++++++++++++++-----------
bcb1e2
 lib/ns/query.c     |  4 +++-
bcb1e2
 2 files changed, 44 insertions(+), 13 deletions(-)
bcb1e2
bcb1e2
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
bcb1e2
index 18585b5..7cbfbb2 100644
bcb1e2
--- a/lib/dns/resolver.c
bcb1e2
+++ b/lib/dns/resolver.c
bcb1e2
@@ -11254,8 +11254,9 @@ void
bcb1e2
 dns_resolver_cancelfetch(dns_fetch_t *fetch) {
bcb1e2
 	fetchctx_t *fctx;
bcb1e2
 	dns_resolver_t *res;
bcb1e2
-	dns_fetchevent_t *event, *next_event;
bcb1e2
-	isc_task_t *etask;
bcb1e2
+	dns_fetchevent_t *event = NULL;
bcb1e2
+	dns_fetchevent_t *event_trystale = NULL;
bcb1e2
+	dns_fetchevent_t *event_fetchdone = NULL;
bcb1e2
 
bcb1e2
 	REQUIRE(DNS_FETCH_VALID(fetch));
bcb1e2
 	fctx = fetch->private;
bcb1e2
@@ -11267,32 +11268,60 @@ dns_resolver_cancelfetch(dns_fetch_t *fetch) {
bcb1e2
 	LOCK(&res->buckets[fctx->bucketnum].lock);
bcb1e2
 
bcb1e2
 	/*
bcb1e2
-	 * Find the completion event for this fetch (as opposed
bcb1e2
+	 * Find the events for this fetch (as opposed
bcb1e2
 	 * to those for other fetches that have joined the same
bcb1e2
-	 * fctx) and send it with result = ISC_R_CANCELED.
bcb1e2
+	 * fctx) and send them with result = ISC_R_CANCELED.
bcb1e2
 	 */
bcb1e2
-	event = NULL;
bcb1e2
 	if (fctx->state != fetchstate_done) {
bcb1e2
+		dns_fetchevent_t *next_event = NULL;
bcb1e2
 		for (event = ISC_LIST_HEAD(fctx->events); event != NULL;
bcb1e2
 		     event = next_event) {
bcb1e2
 			next_event = ISC_LIST_NEXT(event, ev_link);
bcb1e2
 			if (event->fetch == fetch) {
bcb1e2
 				ISC_LIST_UNLINK(fctx->events, event, ev_link);
bcb1e2
-				break;
bcb1e2
+				switch (event->ev_type) {
bcb1e2
+				case DNS_EVENT_TRYSTALE:
bcb1e2
+					INSIST(event_trystale == NULL);
bcb1e2
+					event_trystale = event;
bcb1e2
+					break;
bcb1e2
+				case DNS_EVENT_FETCHDONE:
bcb1e2
+					INSIST(event_fetchdone == NULL);
bcb1e2
+					event_fetchdone = event;
bcb1e2
+					break;
bcb1e2
+				default:
bcb1e2
+					ISC_UNREACHABLE();
bcb1e2
+				}
bcb1e2
+				if (event_trystale != NULL &&
bcb1e2
+				    event_fetchdone != NULL)
bcb1e2
+				{
bcb1e2
+					break;
bcb1e2
+				}
bcb1e2
 			}
bcb1e2
 		}
bcb1e2
 	}
bcb1e2
-	if (event != NULL) {
bcb1e2
-		etask = event->ev_sender;
bcb1e2
-		event->ev_sender = fctx;
bcb1e2
-		event->result = ISC_R_CANCELED;
bcb1e2
-		isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event));
bcb1e2
+
bcb1e2
+	/*
bcb1e2
+	 * The "trystale" event must be sent before the "fetchdone" event,
bcb1e2
+	 * because the latter clears the "recursing" query attribute, which is
bcb1e2
+	 * required by both events (handled by the same callback function).
bcb1e2
+	 */
bcb1e2
+	if (event_trystale != NULL) {
bcb1e2
+		isc_task_t *etask = event_trystale->ev_sender;
bcb1e2
+		event_trystale->ev_sender = fctx;
bcb1e2
+		event_trystale->result = ISC_R_CANCELED;
bcb1e2
+		isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event_trystale));
bcb1e2
 	}
bcb1e2
+	if (event_fetchdone != NULL) {
bcb1e2
+		isc_task_t *etask = event_fetchdone->ev_sender;
bcb1e2
+		event_fetchdone->ev_sender = fctx;
bcb1e2
+		event_fetchdone->result = ISC_R_CANCELED;
bcb1e2
+		isc_task_sendanddetach(&etask, ISC_EVENT_PTR(&event_fetchdone));
bcb1e2
+ 	}
bcb1e2
+
bcb1e2
 	/*
bcb1e2
 	 * The fctx continues running even if no fetches remain;
bcb1e2
 	 * the answer is still cached.
bcb1e2
 	 */
bcb1e2
-
bcb1e2
 	UNLOCK(&res->buckets[fctx->bucketnum].lock);
bcb1e2
 }
bcb1e2
 
bcb1e2
diff --git a/lib/ns/query.c b/lib/ns/query.c
bcb1e2
index f66bab4..4f61374 100644
bcb1e2
--- a/lib/ns/query.c
bcb1e2
+++ b/lib/ns/query.c
bcb1e2
@@ -6021,7 +6021,9 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
bcb1e2
 	CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
bcb1e2
 
bcb1e2
 	if (event->ev_type == DNS_EVENT_TRYSTALE) {
bcb1e2
-		query_lookup_stale(client);
bcb1e2
+		if (devent->result != ISC_R_CANCELED) {
bcb1e2
+			query_lookup_stale(client);
bcb1e2
+		}
bcb1e2
 		isc_event_free(ISC_EVENT_PTR(&event));
bcb1e2
 		return;
bcb1e2
 	}
bcb1e2
-- 
bcb1e2
2.39.1
bcb1e2