From 79c62017eba6b4e81b24e87267ce75689ef89447 Mon Sep 17 00:00:00 2001
From: Diego Fronza <diego@isc.org>
Date: Mon, 21 Sep 2020 17:44:29 -0300
Subject: [PATCH] Properly handling dns_message_t shared references

This commit fix the problems that arose when moving the dns_message_t
object from fetchctx_t to the query structure.

Since the lifetime of query objects are different than that of a
fetchctx and the dns_message_t object held by the query may be being
used by some external module, e.g. validator, even after the query may
have been destroyed, propery handling of the references to the message
were added in this commit to avoid accessing an already destroyed
object.

Specifically, in resquery_response(), a reference to the message is
attached at the beginning of the function and detached at the end, since
a possible call to fctx_cancelquery() would release the dns_message_t
object, and in the next lines of code a call to add_bad() would require
a valid pointer to the same object.

In valcreate() a new reference is attached to the message object, this
ensures that if the corresponding query object is destroyed before the
validator attempts to access it, no invalid pointer access occurs.

In validated() we have to attach a new reference to the message, since
we destroy the validator object at the beginning of the function, and we
need access to the message in the next lines of the same function.
Conflict: adapt is_lame fuction, remove irrelevant code
Reference: https://gitlab.isc.org/isc-projects/bind9/-/commit/79c62017eba6b4e81b24e87267ce75689ef89447
---
 lib/dns/resolver.c    | 266 ++++++++++++++++++++----------------------
 7 files changed, 517 insertions(+), 268 deletions(-)

diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index ec5293baad..e3f9aef95c 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -629,7 +629,7 @@ valcreate(fetchctx_t *fctx, dns_message_t *rmessage,
 
 	valarg->fctx = fctx;
 	valarg->addrinfo = addrinfo;
-	valarg->rmessage = rmessage;
+	dns_message_attach(rmessage, &valarg->rmessage);
 
 	if (!ISC_LIST_EMPTY(fctx->validators))
 		valoptions |= DNS_VALIDATOR_DEFER;
@@ -647,8 +647,10 @@ valcreate(fetchctx_t *fctx, dns_message_t *rmessage,
 			fctx->validator = validator;
 		}
 		ISC_LIST_APPEND(fctx->validators, validator, link);
-	} else
+	} else {
+		dns_message_detach(&valarg->rmessage);
 		isc_mem_put(fctx->mctx, valarg, sizeof(*valarg));
+	}
 	return (result);
 }
 
@@ -1004,7 +1006,7 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
 			    ISFORWARDER(query->addrinfo))
 			{
 				add_bad(fctx, query->rmessage,
-				        query->addrinfo, ISC_R_TIMEDOUT,
+					query->addrinfo, ISC_R_TIMEDOUT,
 					badns_forwarder);
 			}
 
@@ -1736,7 +1738,7 @@ fctx_query(fetchctx_t *fctx, dns_adbaddrinfo_t *addrinfo,
 	}
 	query->rmessage = NULL;
 	result = dns_message_create(fctx->mctx, DNS_MESSAGE_INTENTPARSE,
-                                   &query->rmessage);
+				   &query->rmessage);
 	if (result != ISC_R_SUCCESS) {
 		goto cleanup_query;
 	}
@@ -4897,7 +4899,7 @@ validated(isc_task_t *task, isc_event_t *event) {
 	uint32_t ttl;
 	unsigned options;
 	uint32_t bucketnum;
-	dns_message_t *rmessage;
+	dns_message_t *rmessage = NULL;
 
 	UNUSED(task); /* for now */
 
@@ -4908,7 +4910,7 @@ validated(isc_task_t *task, isc_event_t *event) {
 	res = fctx->res;
 	addrinfo = valarg->addrinfo;
 	REQUIRE(!ISC_LIST_EMPTY(fctx->validators));
-	rmessage = valarg->rmessage;
+	dns_message_attach(valarg->rmessage, &rmessage);
 
 	vevent = (dns_validatorevent_t *)event;
 	fctx->vresult = vevent->result;
@@ -4927,6 +4929,7 @@ validated(isc_task_t *task, isc_event_t *event) {
 	 * destroy the fctx if necessary.
 	 */
 	dns_validator_destroy(&vevent->validator);
+	dns_message_detach(&valarg->rmessage);
 	isc_mem_put(fctx->mctx, valarg, sizeof(*valarg));
 
 	negative = (vevent->rdataset == NULL);
@@ -5065,9 +5068,10 @@ validated(isc_task_t *task, isc_event_t *event) {
 				dns_resolver_addbadcache(res, &fctx->name,
 							 fctx->type, &expire);
 			fctx_done(fctx, result, __LINE__); /* Locks bucket. */
-		} else
+		} else {
 			fctx_try(fctx, true, true); /* Locks bucket. */
-		return;
+		}
+		goto cleanup_rmessage;
 	}
 
 
@@ -5287,6 +5291,8 @@ validated(isc_task_t *task, isc_event_t *event) {
  cleanup_event:
 	INSIST(node == NULL);
 	isc_event_free(&event);
+ cleanup_rmessage:
+	dns_message_detach(&rmessage);
 }
 
 static void
@@ -6274,9 +6280,6 @@ check_section(void *arg, dns_name_t *addname, dns_rdatatype_t type,
 		}
 	}
 
-	dns_message_detach(&chkarg->rmessage);
-	isc_mem_put(fctx->mctx, chkarg, sizeof(*chkarg));
-
 	return (ISC_R_SUCCESS);
 }
 
@@ -6318,15 +6321,13 @@ chase_additional(fetchctx_t *fctx, dns_message_t *rmessage) {
 		     rdataset != NULL;
 		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
 			if (CHASE(rdataset)) {
-				dns_chkarg_t *chkarg;
-				chkarg = isc_mem_get(fctx->mctx,
-						     sizeof(*chkarg));
-				chkarg->fctx = fctx;
-				dns_message_attach(rmessage, &chkarg->rmessage);
+				dns_chkarg_t chkarg;
+				chkarg.fctx = fctx;
+				chkarg.rmessage = rmessage;
 				rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
 				(void)dns_rdataset_additionaldata(rdataset,
 								  check_related,
-								  chkarg);
+								  &chkarg);
 				rescan = true;
 			}
 		}
@@ -6928,7 +6929,7 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message,
 	 * we're not following a chain.)
 	 */
 	if (!negative_response && ns_name != NULL && oqname == NULL) {
-		dns_chkarg_t *chkarg;
+		dns_chkarg_t chkarg;
 		/*
 		 * We already know ns_name is a subdomain of fctx->domain.
 		 * If ns_name is equal to fctx->domain, we're not making
@@ -6958,12 +6959,10 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message,
 		 */
 		INSIST(ns_rdataset != NULL);
 		FCTX_ATTR_SET(fctx, FCTX_ATTR_GLUING);
-		chkarg = isc_mem_get(fctx->mctx,
-				     sizeof(*chkarg));
-		chkarg->fctx = fctx;
-		dns_message_attach(message, &chkarg->rmessage);
+		chkarg.fctx = fctx;
+		chkarg.rmessage = message;
 		(void)dns_rdataset_additionaldata(ns_rdataset, check_related,
-						  chkarg);
+						  &chkarg);
 #if CHECK_FOR_GLUE_IN_ANSWER
 		/*
 		 * Look in the answer section for "glue" that is incorrectly
@@ -6975,12 +6974,11 @@ noanswer_response(fetchctx_t *fctx, dns_message_t *message,
 		if ((look_in_options & LOOK_FOR_GLUE_IN_ANSWER) != 0 &&
 		    (fctx->type == dns_rdatatype_aaaa ||
 		     fctx->type == dns_rdatatype_a)) {
-			dns_chkarg_t *chkarg;
-			chkarg = isc_mem_get(fctx->mctx, sizeof(*chkarg));
-			chkarg->fctx = fctx;
-			dns_message_attach(message, &chkarg->rmessage);
+			dns_chkarg_t chkarg;
+			chkarg.fcx = fctx;
+			chkarg.rmessage = message;
 			(void)dns_rdataset_additionaldata(ns_rdataset,
-							  check_answer, chkarg);
+							  check_answer, &chkarg);
 		}
 #endif
 		FCTX_ATTR_CLR(fctx, FCTX_ATTR_GLUING);
@@ -7182,7 +7180,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
 		     rdataset != NULL;
 		     rdataset = ISC_LIST_NEXT(rdataset, link))
 		{
-			dns_chkarg_t *chkarg;
+			dns_chkarg_t chkarg;
 			if (!validinanswer(rdataset, fctx)) {
 				return (DNS_R_FORMERR);
 			}
@@ -7211,16 +7209,14 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
 			rdataset->attributes |= DNS_RDATASETATTR_CACHE;
 			rdataset->trust = trust;
 			rdataset->attributes &= ~DNS_RDATASETATTR_CHASE;
-			chkarg = isc_mem_get(fctx->mctx,
-					     sizeof(*chkarg));
-			chkarg->fctx = fctx;
-			dns_message_attach(message, &chkarg->rmessage);
+			chkarg.fctx = fctx;
+			chkarg.rmessage = message;
 			(void)dns_rdataset_additionaldata(rdataset,
 							  check_related,
-							  chkarg);
+							  &chkarg);
 		}
 	} else if (aname != NULL) {
-		dns_chkarg_t *chkarg;
+		dns_chkarg_t chkarg;
 		if (!validinanswer(ardataset, fctx))
 			return (DNS_R_FORMERR);
 		if ((ardataset->type == dns_rdatatype_a ||
@@ -7242,12 +7238,10 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
 		ardataset->attributes |= DNS_RDATASETATTR_ANSWER;
 		ardataset->attributes |= DNS_RDATASETATTR_CACHE;
 		ardataset->trust = trust;
-		chkarg = isc_mem_get(fctx->mctx,
-				     sizeof(*chkarg));
-		chkarg->fctx = fctx;
-		dns_message_attach(message, &chkarg->rmessage);
+		chkarg.fctx = fctx;
+		chkarg.rmessage = message;
 		(void)dns_rdataset_additionaldata(ardataset, check_related,
-						  chkarg);
+						  &chkarg);
 		for (sigrdataset = ISC_LIST_HEAD(aname->list);
 		     sigrdataset != NULL;
 		     sigrdataset = ISC_LIST_NEXT(sigrdataset, link)) {
@@ -7385,7 +7379,7 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
 				if (rdataset->type == dns_rdatatype_ns ||
 				    (rdataset->type == dns_rdatatype_rrsig &&
 				     rdataset->covers == dns_rdatatype_ns)) {
-					dns_chkarg_t *chkarg;
+					dns_chkarg_t chkarg;
 					name->attributes |=
 						DNS_NAMEATTR_CACHE;
 					rdataset->attributes |=
@@ -7407,15 +7401,12 @@ answer_response(fetchctx_t *fctx, dns_message_t *message) {
 					 * Mark any additional data related
 					 * to this rdataset.
 					 */
-					chkarg = isc_mem_get(fctx->mctx,
-							     sizeof(*chkarg));
-					chkarg->fctx = fctx;
-					dns_message_attach(message,
-							   &chkarg->rmessage);
+					chkarg.fctx = fctx;
+					chkarg.rmessage = message;
 					(void)dns_rdataset_additionaldata(
 							rdataset,
 							check_related,
-							chkarg);
+							&chkarg);
 					done = true;
 				}
 			}
@@ -7835,7 +7826,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	dns_dispatchevent_t *devent = (dns_dispatchevent_t *)event;
 	bool keep_trying, get_nameservers, resend, nextitem;
 	bool truncated;
-	dns_message_t *message;
+	dns_message_t *rmessage = NULL;
 	dns_rdataset_t *opt;
 	fetchctx_t *fctx;
 	dns_name_t *fname;
@@ -7866,6 +7857,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	options = query->options;
 	REQUIRE(VALID_FCTX(fctx));
 	REQUIRE(event->ev_type == DNS_EVENT_DISPATCH);
+	dns_message_attach(query->rmessage, &rmessage);
 
 	QTRACE("response");
 
@@ -7947,10 +7939,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		goto done;
 	}
 
-	message = query->rmessage;
-
 	if (query->tsig != NULL) {
-		result = dns_message_setquerytsig(message, query->tsig);
+		result = dns_message_setquerytsig(rmessage, query->tsig);
 		if (result != ISC_R_SUCCESS) {
 			FCTXTRACE3("unable to set query tsig", result);
 			goto done;
@@ -7958,14 +7948,14 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	}
 
 	if (query->tsigkey) {
-		result = dns_message_settsigkey(message, query->tsigkey);
+		result = dns_message_settsigkey(rmessage, query->tsigkey);
 		if (result != ISC_R_SUCCESS) {
 			FCTXTRACE3("unable to set tsig key", result);
 			goto done;
 		}
 	}
 
-	dns_message_setclass(message, res->rdclass);
+	dns_message_setclass(rmessage, res->rdclass);
 
 	if ((options & DNS_FETCHOPT_TCP) == 0) {
 		if ((options & DNS_FETCHOPT_NOEDNS0) == 0)
@@ -7974,16 +7964,16 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		else
 			dns_adb_plainresponse(fctx->adb, query->addrinfo);
 	}
-	result = dns_message_parse(message, &devent->buffer, 0);
+	result = dns_message_parse(rmessage, &devent->buffer, 0);
 	if (result != ISC_R_SUCCESS) {
 		FCTXTRACE3("message failed to parse", result);
 		switch (result) {
 		case ISC_R_UNEXPECTEDEND:
-			if (!message->question_ok ||
-			    (message->flags & DNS_MESSAGEFLAG_TC) == 0 ||
+			if (!rmessage->question_ok ||
+			    (rmessage->flags & DNS_MESSAGEFLAG_TC) == 0 ||
 			    (options & DNS_FETCHOPT_TCP) != 0) {
 				/*
-				 * Either the message ended prematurely,
+				 * Either the rmessage ended prematurely,
 				 * and/or wasn't marked as being truncated,
 				 * and/or this is a response to a query we
 				 * sent over TCP.  In all of these cases,
@@ -8012,7 +8002,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			}
 			/*
 			 * We defer retrying via TCP for a bit so we can
-			 * check out this message further.
+			 * check out this rmessage further.
 			 */
 			truncated = true;
 			break;
@@ -8043,7 +8033,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Log the incoming packet.
 	 */
-	dns_message_logfmtpacket2(message, "received packet from",
+	dns_message_logfmtpacket2(rmessage, "received packet from",
 				  &query->addrinfo->sockaddr,
 				  DNS_LOGCATEGORY_RESOLVER,
 				  DNS_LOGMODULE_PACKETS,
@@ -8089,7 +8079,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		    &zr, &query->start, NULL, &devent->buffer);
 #endif /* HAVE_DNSTAP */
 
-	if (message->rdclass != res->rdclass) {
+	if (rmessage->rdclass != res->rdclass) {
 		resend = true;
 		FCTXTRACE("bad class");
 		goto done;
@@ -8098,11 +8088,11 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Process receive opt record.
 	 */
-	opt = dns_message_getopt(message);
+	opt = dns_message_getopt(rmessage);
 	if (opt != NULL)
 		process_opt(query, opt);
 
-	if (message->cc_bad && (options & DNS_FETCHOPT_TCP) == 0) {
+	if (rmessage->cc_bad && (options & DNS_FETCHOPT_TCP) == 0) {
 		/*
 		 * If the COOKIE is bad, assume it is an attack and
 		 * keep listening for a good answer.
@@ -8125,10 +8115,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	 * the same question.
 	 * FORMERR/NOTIMP if they have a question section then it must match.
 	 */
-	switch (message->rcode) {
+	switch (rmessage->rcode) {
 	case dns_rcode_notimp:
 	case dns_rcode_formerr:
-		if (message->counts[DNS_SECTION_QUESTION] == 0)
+		if (rmessage->counts[DNS_SECTION_QUESTION] == 0)
 			break;
 		/* FALLTHROUGH */
 	case dns_rcode_nxrrset:	/* Not expected. */
@@ -8139,7 +8129,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	case dns_rcode_refused:
 	case dns_rcode_servfail:
 	default:
-		result = same_question(fctx, message);
+		result = same_question(fctx, rmessage);
 		if (result != ISC_R_SUCCESS) {
 			FCTXTRACE3("response did not match question", result);
 			nextitem = true;
@@ -8149,10 +8139,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	}
 
 	/*
-	 * If the message is signed, check the signature.  If not, this
+	 * If the rmessage is signed, check the signature.  If not, this
 	 * returns success anyway.
 	 */
-	result = dns_message_checksig(message, res->view);
+	result = dns_message_checksig(rmessage, res->view);
 	if (result != ISC_R_SUCCESS) {
 		FCTXTRACE3("signature check failed", result);
 		goto done;
@@ -8161,12 +8151,12 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * The dispatcher should ensure we only get responses with QR set.
 	 */
-	INSIST((message->flags & DNS_MESSAGEFLAG_QR) != 0);
+	INSIST((rmessage->flags & DNS_MESSAGEFLAG_QR) != 0);
 	/*
-	 * INSIST() that the message comes from the place we sent it to,
+	 * INSIST() that the rmessage comes from the place we sent it to,
 	 * since the dispatch code should ensure this.
 	 *
-	 * INSIST() that the message id is correct (this should also be
+	 * INSIST() that the rmessage id is correct (this should also be
 	 * ensured by the dispatch code).
 	 */
 
@@ -8177,12 +8167,12 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	 * EDNS support.
 	 */
 	if (opt == NULL && !EDNSOK(query->addrinfo) &&
-	    (message->rcode == dns_rcode_noerror ||
-	     message->rcode == dns_rcode_nxdomain ||
-	     message->rcode == dns_rcode_refused ||
-	     message->rcode == dns_rcode_yxdomain) &&
+	    (rmessage->rcode == dns_rcode_noerror ||
+	     rmessage->rcode == dns_rcode_nxdomain ||
+	     rmessage->rcode == dns_rcode_refused ||
+	     rmessage->rcode == dns_rcode_yxdomain) &&
 	     bad_edns(fctx, &query->addrinfo->sockaddr)) {
-		dns_message_logpacket2(message,
+		dns_message_logpacket2(rmessage,
 				       "received packet (bad edns) from",
 				       &query->addrinfo->sockaddr,
 				       DNS_LOGCATEGORY_RESOLVER,
@@ -8192,10 +8182,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		dns_adb_changeflags(fctx->adb, query->addrinfo,
 				    DNS_FETCHOPT_NOEDNS0,
 				    DNS_FETCHOPT_NOEDNS0);
-	} else if (opt == NULL && (message->flags & DNS_MESSAGEFLAG_TC) == 0 &&
+	} else if (opt == NULL && (rmessage->flags & DNS_MESSAGEFLAG_TC) == 0 &&
 		   !EDNSOK(query->addrinfo) &&
-		   (message->rcode == dns_rcode_noerror ||
-		    message->rcode == dns_rcode_nxdomain) &&
+		   (rmessage->rcode == dns_rcode_noerror ||
+		    rmessage->rcode == dns_rcode_nxdomain) &&
 		   (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
 		/*
 		 * We didn't get a OPT record in response to a EDNS query.
@@ -8208,7 +8198,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		 * should be safe to do for any rcode we limit it to NOERROR
 		 * and NXDOMAIN.
 		 */
-		dns_message_logpacket2(message, "received packet (no opt) from",
+		dns_message_logpacket2(rmessage, "received packet (no opt) from",
 				       &query->addrinfo->sockaddr,
 				       DNS_LOGCATEGORY_RESOLVER,
 				       DNS_LOGMODULE_RESOLVER,
@@ -8224,10 +8214,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	 */
 	if (opt != NULL && !EDNSOK(query->addrinfo) &&
 	    (query->options & DNS_FETCHOPT_NOEDNS0) == 0 &&
-	    (message->rcode == dns_rcode_noerror ||
-	     message->rcode == dns_rcode_nxdomain ||
-	     message->rcode == dns_rcode_refused ||
-	     message->rcode == dns_rcode_yxdomain)) {
+	    (rmessage->rcode == dns_rcode_noerror ||
+	     rmessage->rcode == dns_rcode_nxdomain ||
+	     rmessage->rcode == dns_rcode_refused ||
+	     rmessage->rcode == dns_rcode_yxdomain)) {
 		dns_adb_changeflags(fctx->adb, query->addrinfo,
 				    FCTX_ADDRINFO_EDNSOK,
 				    FCTX_ADDRINFO_EDNSOK);
@@ -8236,7 +8226,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Deal with truncated responses by retrying using TCP.
 	 */
-	if ((message->flags & DNS_MESSAGEFLAG_TC) != 0)
+	if ((rmessage->flags & DNS_MESSAGEFLAG_TC) != 0)
 		truncated = true;
 
 	if (truncated) {
@@ -8255,19 +8245,19 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Is it a query response?
 	 */
-	if (message->opcode != dns_opcode_query) {
+	if (rmessage->opcode != dns_opcode_query) {
 		/* XXXRTH Log */
 		broken_server = DNS_R_UNEXPECTEDOPCODE;
 		keep_trying = true;
-		FCTXTRACE("invalid message opcode");
+		FCTXTRACE("invalid rmessage opcode");
 		goto done;
 	}
 
 	/*
 	 * Update statistics about erroneous responses.
 	 */
-	if (message->rcode != dns_rcode_noerror) {
-		switch (message->rcode) {
+	if (rmessage->rcode != dns_rcode_noerror) {
+		switch (rmessage->rcode) {
 		case dns_rcode_nxdomain:
 			inc_stats(res, dns_resstatscounter_nxdomain);
 			break;
@@ -8295,9 +8285,9 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Is the remote server broken, or does it dislike us?
 	 */
-	if (message->rcode != dns_rcode_noerror &&
-	    message->rcode != dns_rcode_yxdomain &&
-	    message->rcode != dns_rcode_nxdomain) {
+	if (rmessage->rcode != dns_rcode_noerror &&
+	    rmessage->rcode != dns_rcode_yxdomain &&
+	    rmessage->rcode != dns_rcode_nxdomain) {
 		isc_buffer_t b;
 		char code[64];
 		unsigned char cookie[64];
@@ -8306,19 +8296,19 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		 * Some servers do not ignore unknown EDNS options.
 		 */
 		if (!NOCOOKIE(query->addrinfo) &&
-		    (message->rcode == dns_rcode_formerr ||
-		     message->rcode == dns_rcode_notimp ||
-		     message->rcode == dns_rcode_refused) &&
+		    (rmessage->rcode == dns_rcode_formerr ||
+		     rmessage->rcode == dns_rcode_notimp ||
+		     rmessage->rcode == dns_rcode_refused) &&
 		     dns_adb_getcookie(fctx->adb, query->addrinfo,
 				       cookie, sizeof(cookie)) == 0U) {
 			dns_adb_changeflags(fctx->adb, query->addrinfo,
 					    FCTX_ADDRINFO_NOCOOKIE,
 					    FCTX_ADDRINFO_NOCOOKIE);
 			resend = true;
-		} else if ((message->rcode == dns_rcode_formerr ||
-			    message->rcode == dns_rcode_notimp ||
-			    (message->rcode == dns_rcode_servfail &&
-			     dns_message_getopt(message) == NULL)) &&
+		} else if ((rmessage->rcode == dns_rcode_formerr ||
+			    rmessage->rcode == dns_rcode_notimp ||
+			    (rmessage->rcode == dns_rcode_servfail &&
+			     dns_message_getopt(rmessage) == NULL)) &&
 			   (query->options & DNS_FETCHOPT_NOEDNS0) == 0) {
 			/*
 			 * It's very likely they don't like EDNS0.
@@ -8338,7 +8328,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			 */
 			add_bad_edns(fctx, &query->addrinfo->sockaddr);
 			inc_stats(res, dns_resstatscounter_edns0fail);
-		} else if (message->rcode == dns_rcode_formerr) {
+		} else if (rmessage->rcode == dns_rcode_formerr) {
 			if (ISFORWARDER(query->addrinfo)) {
 				/*
 				 * This forwarder doesn't understand us,
@@ -8358,7 +8348,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				log_formerr(fctx, "server sent FORMERR");
 				result = DNS_R_FORMERR;
 			}
-		} else if (message->rcode == dns_rcode_badvers) {
+		} else if (rmessage->rcode == dns_rcode_badvers) {
 			unsigned int version;
 			bool setnocookie = false;
 #if DNS_EDNS_VERSION > 0
@@ -8425,8 +8415,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				keep_trying = true;
 			}
 #endif
-		} else if (message->rcode == dns_rcode_badcookie &&
-			   message->cc_ok) {
+		} else if (rmessage->rcode == dns_rcode_badcookie &&
+			   rmessage->cc_ok) {
 			/*
 			 * We have recorded the new cookie.
 			 */
@@ -8444,7 +8434,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		}
 
 		isc_buffer_init(&b, code, sizeof(code) - 1);
-		dns_rcode_totext(message->rcode, &b);
+		dns_rcode_totext(rmessage->rcode, &b);
 		code[isc_buffer_usedlength(&b)] = '\0';
 		FCTXTRACE2("remote server broken: returned ", code);
 		goto done;
@@ -8454,7 +8444,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Is the server lame?
 	 */
-	if (!ISFORWARDER(query->addrinfo) && is_lame(fctx, message)) {
+	if (!ISFORWARDER(query->addrinfo) && is_lame(fctx, rmessage)) {
 		inc_stats(res, dns_resstatscounter_lame);
 		log_lame(fctx, query->addrinfo);
 		if (res->lame_ttl != 0) {
@@ -8477,7 +8467,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	if (!ISFORWARDER(query->addrinfo) &&
 	    dns_view_isdelegationonly(res->view, &fctx->domain) &&
 	    !dns_name_equal(&fctx->domain, &fctx->name) &&
-	    fix_mustbedelegationornxdomain(message, fctx)) {
+	    fix_mustbedelegationornxdomain(rmessage, fctx)) {
 		char namebuf[DNS_NAME_FORMATSIZE];
 		char domainbuf[DNS_NAME_FORMATSIZE];
 		char addrbuf[ISC_SOCKADDR_FORMATSIZE];
@@ -8500,7 +8490,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	}
 
 	if ((res->options & DNS_RESOLVER_CHECKNAMES) != 0)
-		checknames(message);
+		checknames(rmessage);
 
 	/*
 	 * Clear cache bits.
@@ -8510,22 +8500,22 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Did we get any answers?
 	 */
-	if (message->counts[DNS_SECTION_ANSWER] > 0 &&
-	    (message->rcode == dns_rcode_noerror ||
-	     message->rcode == dns_rcode_yxdomain ||
-	     message->rcode == dns_rcode_nxdomain)) {
+	if (rmessage->counts[DNS_SECTION_ANSWER] > 0 &&
+	    (rmessage->rcode == dns_rcode_noerror ||
+	     rmessage->rcode == dns_rcode_yxdomain ||
+	     rmessage->rcode == dns_rcode_nxdomain)) {
 		/*
 		 * [normal case]
 		 * We've got answers.  If it has an authoritative answer or an
 		 * answer from a forwarder, we're done.
 		 */
-		if ((message->flags & DNS_MESSAGEFLAG_AA) != 0 ||
+		if ((rmessage->flags & DNS_MESSAGEFLAG_AA) != 0 ||
 		    ISFORWARDER(query->addrinfo))
 		{
-			result = answer_response(fctx, message);
+			result = answer_response(fctx, rmessage);
 			if (result != ISC_R_SUCCESS)
 				FCTXTRACE3("answer_response (AA/fwd)", result);
-		} else if (iscname(fctx, message) &&
+		} else if (iscname(fctx, rmessage) &&
 			 fctx->type != dns_rdatatype_any &&
 			 fctx->type != dns_rdatatype_cname)
 		{
@@ -8534,16 +8524,16 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			 * answer when a CNAME is followed.  We should treat
 			 * it as a valid answer.
 			 */
-			result = answer_response(fctx, message);
+			result = answer_response(fctx, rmessage);
 			if (result != ISC_R_SUCCESS)
 				FCTXTRACE3("answer_response (!ANY/!CNAME)",
 					   result);
 		} else if (fctx->type != dns_rdatatype_ns &&
-			   !betterreferral(fctx, message)) {
+			   !betterreferral(fctx, rmessage)) {
 			/*
 			 * Lame response !!!.
 			 */
-			result = answer_response(fctx, message);
+			result = answer_response(fctx, rmessage);
 			if (result != ISC_R_SUCCESS)
 				FCTXTRACE3("answer_response (!NS)", result);
 		} else {
@@ -8557,7 +8547,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				 * special kludge to treat it as a referral.
 				 */
 				result = noanswer_response(fctx,
-							   message,
+							   rmessage,
 							   NULL,
 						  LOOK_FOR_NS_IN_ANSWER);
 				if (result != ISC_R_SUCCESS)
@@ -8577,7 +8567,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				 * such a corner case.
 				 */
 				result = noanswer_response(fctx,
-							   message,
+							   rmessage,
 							   NULL,
 						   LOOK_FOR_GLUE_IN_ANSWER);
 				if (result != ISC_R_SUCCESS)
@@ -8602,13 +8592,13 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				keep_trying = true;
 			goto done;
 		}
-	} else if (message->counts[DNS_SECTION_AUTHORITY] > 0 ||
-		   message->rcode == dns_rcode_noerror ||
-		   message->rcode == dns_rcode_nxdomain) {
+	} else if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0 ||
+		   rmessage->rcode == dns_rcode_noerror ||
+		   rmessage->rcode == dns_rcode_nxdomain) {
 		/*
 		 * NXDOMAIN, NXRDATASET, or referral.
 		 */
-		result = noanswer_response(fctx, message, NULL, 0);
+		result = noanswer_response(fctx, rmessage, NULL, 0);
 		switch (result) {
 		case ISC_R_SUCCESS:
 		case DNS_R_CHASEDSSERVERS:
@@ -8664,14 +8654,14 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	/*
 	 * Follow additional section data chains.
 	 */
-	chase_additional(fctx, message);
+	chase_additional(fctx, rmessage);
 
 	/*
-	 * Cache the cacheable parts of the message.  This may also cause
+	 * Cache the cacheable parts of the rmessage.  This may also cause
 	 * work to be queued to the DNSSEC validator.
 	 */
 	if (WANTCACHE(fctx)) {
-		result = cache_message(fctx, message, query->addrinfo,
+		result = cache_message(fctx, rmessage, query->addrinfo,
 				       now);
 		if (result != ISC_R_SUCCESS) {
 			FCTXTRACE3("cache_message complete", result);
@@ -8680,7 +8670,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 	}
 
 	/*
-	 * Ncache the negatively cacheable parts of the message.  This may
+	 * Ncache the negatively cacheable parts of the rmessage.  This may
 	 * also cause work to be queued to the DNSSEC validator.
 	 */
 	if (WANTNCACHE(fctx)) {
@@ -8689,16 +8679,16 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		/*
 		 * Cache DS NXDOMAIN separately to other types.
 		 */
-		if (message->rcode == dns_rcode_nxdomain &&
+		if (rmessage->rcode == dns_rcode_nxdomain &&
 		    fctx->type != dns_rdatatype_ds)
 			covers = dns_rdatatype_any;
 		else
 			covers = fctx->type;
 
 		/*
-		 * Cache any negative cache entries in the message.
+		 * Cache any negative cache entries in the rmessage.
 		 */
-		result = ncache_message(fctx, message, query->addrinfo, covers, now);
+		result = ncache_message(fctx, rmessage, query->addrinfo, covers, now);
 		if (result != ISC_R_SUCCESS)
 			FCTXTRACE3("ncache_message complete", result);
 	}
@@ -8726,7 +8716,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 #ifdef ENABLE_AFL
 	if (dns_fuzzing_resolver && (keep_trying || resend)) {
 		fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
-		return;
+		goto cleanup_rmessage;
 	} else
 #endif
 	if (keep_trying) {
@@ -8737,7 +8727,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			 * Add this server to the list of bad servers for
 			 * this fctx.
 			 */
-			add_bad(fctx, message, addrinfo,
+			add_bad(fctx, rmessage, addrinfo,
 				broken_server, broken_type);
 		}
 
@@ -8746,7 +8736,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			fname = dns_fixedname_initname(&foundname);
 			if (result != ISC_R_SUCCESS) {
 				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
-				return;
+				goto detach_rmessage;
 			}
 			findoptions = 0;
 			if (dns_rdatatype_atparent(fctx->type))
@@ -8764,7 +8754,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			if (result != ISC_R_SUCCESS) {
 				FCTXTRACE("couldn't find a zonecut");
 				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
-				return;
+				goto detach_rmessage;
 			}
 			if (!dns_name_issubdomain(fname, &fctx->domain)) {
 				/*
@@ -8773,7 +8763,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 				 */
 				FCTXTRACE("nameservers now above QDOMAIN");
 				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
-				return;
+				goto detach_rmessage;
 			}
 
 			fcount_decr(fctx);
@@ -8782,12 +8772,12 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			result = dns_name_dup(fname, fctx->mctx, &fctx->domain);
 			if (result != ISC_R_SUCCESS) {
 				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
-				return;
+				goto detach_rmessage;
 			}
 			result = fcount_incr(fctx, true);
 			if (result != ISC_R_SUCCESS) {
 				fctx_done(fctx, DNS_R_SERVFAIL, __LINE__);
-				return;
+				goto detach_rmessage;
 			}
 			fctx->ns_ttl = fctx->nameservers.ttl;
 			fctx->ns_ttl_ok = true;
@@ -8844,7 +8834,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 			fctx_done(fctx, result, __LINE__);
 	} else if (result == DNS_R_CHASEDSSERVERS) {
 		unsigned int n;
-		add_bad(fctx, query->rmessage, addrinfo, result, broken_type);
+		add_bad(fctx, rmessage, addrinfo, result, broken_type);
 		fctx_cancelqueries(fctx, true, false);
 		fctx_cleanupfinds(fctx);
 		fctx_cleanupforwaddrs(fctx);
@@ -8875,6 +8865,8 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 		 */
 		fctx_done(fctx, result, __LINE__);
 	}
+detach_rmessage:
+	dns_message_detach(&rmessage);
 }

 /***
-- 
2.23.0
