From 1f2d2611ee23f0b9200dbea8a7b65fa9f3b86e3f Mon Sep 17 00:00:00 2001
From: Aram Sargsyan <aram@isc.org>
Date: Tue, 12 Apr 2022 13:00:45 +0000
Subject: [PATCH] Fix dig +nssearch race between recv_done() and send_done()

The `send_done()` callback needs to access query's `link.next` pointer
when running in `+nssearch` mode, even if the query is already canceled
or serviced, which can happen when `recv_done()` happens to be called
earlier than `send_done()`.

Keep the next query's pointer before unlinking the query from the
lookup's queries list in `clear_query()` so that `send_done()` can
use it even if the query is cleared.
Conflict: NA
Reference: https://gitlab.isc.org/isc-projects/bind9/-/commit/1f2d2611ee23f0b9200dbea8a7b65fa9f3b86e3f
---
 bin/dig/dighost.c         | 8 ++++++--
 bin/dig/include/dig/dig.h | 1 +
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index 27af3c80f1..e0ba9c2aae 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -1591,6 +1591,7 @@ clear_query(dig_query_t *query) {
 	}
 
 	if (ISC_LINK_LINKED(query, link)) {
+		query->saved_next = ISC_LIST_NEXT(query, link);
 		ISC_LIST_UNLINK(lookup->q, query, link);
 	}
 	if (ISC_LINK_LINKED(query, clink)) {
@@ -1609,6 +1610,7 @@ clear_query(dig_query_t *query) {
 	isc_buffer_invalidate(&query->lengthbuf);
 
 	if (query->waiting_senddone) {
+		debug("waiting senddone, delay freeing query");
 		query->pending_free = true;
 	} else {
 		query->magic = 0;
@@ -2583,6 +2585,7 @@ setup_lookup(dig_lookup_t *lookup) {
 
 		ISC_LINK_INIT(query, clink);
 		ISC_LINK_INIT(query, link);
+		query->saved_next = NULL;
 
 		query->magic = DIG_QUERY_MAGIC;
 
@@ -2617,10 +2620,11 @@ send_done(isc_task_t *_task, isc_event_t *event) {
 	query->waiting_senddone = false;
 	l = query->lookup;
 
-	if (!query->pending_free && l->ns_search_only && !l->trace_root &&
+	if (l == current_lookup && l->ns_search_only && !l->trace_root &&
 	    !l->tcp_mode) {
 		debug("sending next, since searching");
-		next = ISC_LIST_NEXT(query, link);
+		next = query->pending_free ? query->saved_next
+					   : ISC_LIST_NEXT(query, link);
 		if (next != NULL) {
 			send_udp(next);
 		}
diff --git a/bin/dig/include/dig/dig.h b/bin/dig/include/dig/dig.h
index 79a868d1ef..9683e39892 100644
--- a/bin/dig/include/dig/dig.h
+++ b/bin/dig/include/dig/dig.h
@@ -183,6 +183,7 @@ struct dig_query {
 	isc_socket_t *sock;
 	ISC_LINK(dig_query_t) link;
 	ISC_LINK(dig_query_t) clink;
+	dig_query_t   *saved_next;
 	isc_sockaddr_t sockaddr;
 	isc_time_t     time_sent;
 	isc_time_t     time_recv;
-- 
2.23.0

