From c12608ca934c0433d280e65fe6c631013e200cfe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@isc.org>
Date: Thu, 11 Jan 2024 12:03:24 +0100
Subject: [PATCH] Split fast and slow task queues

Change the taskmgr (and thus netmgr) in a way that it supports fast and
slow task queues.  The fast queue is used for incoming DNS traffic and
it will pass the processing to the slow queue for sending outgoing DNS
messages and processing resolver messages.

In the future, more tasks might get moved to the slow queues, so the
cached and authoritative DNS traffic can be handled without being slowed
down by operations that take longer time to process.

Conflict:Since the optimization patch 32a3970b has not been incorporated, the patch test modification part needs to be incorporated.
Reference:https://downloads.isc.org/isc/bind/9.16.48/patches/0005-CVE-2023-50387-CVE-2023-50868.patch

(cherry picked from commit 1b3b0cef224e7a9e8279c5cfe2f7e188e3777cc7)

---
 lib/dns/dst_api.c               | 27 +++++++++----
 lib/dns/include/dns/validator.h |  1 +
 lib/dns/include/dst/dst.h       |  4 ++
 lib/dns/resolver.c              |  4 +-
 lib/dns/validator.c             | 67 +++++++++++++++------------------
 lib/isc/include/isc/netmgr.h    |  3 ++
 lib/isc/netmgr/netmgr-int.h     |  1 +
 lib/isc/netmgr/netmgr.c         | 36 +++++++++++-------
 lib/isc/netmgr/tcp.c            |  6 +--
 lib/isc/netmgr/tcpdns.c         |  4 +-
 lib/isc/netmgr/udp.c            |  6 +--
 lib/isc/tests/netmgr_test.c     |  2 -
 12 files changed, 91 insertions(+), 70 deletions(-)

diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c
index c2977e0..0bf84b9 100644
--- a/lib/dns/dst_api.c
+++ b/lib/dns/dst_api.c
@@ -160,7 +160,8 @@ computeid(dst_key_t *key);
 static isc_result_t
 frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
 	   unsigned int protocol, dns_rdataclass_t rdclass,
-	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
+	   isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
+	   dst_key_t **keyp);
 
 static isc_result_t
 algorithm_status(unsigned int alg);
@@ -776,6 +777,13 @@ dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
 isc_result_t
 dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
 		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
+	return (dst_key_fromdns_ex(name, rdclass, source, mctx, false, keyp));
+}
+
+isc_result_t
+dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass,
+		   isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
+		   dst_key_t **keyp) {
 	uint8_t alg, proto;
 	uint32_t flags, extflags;
 	dst_key_t *key = NULL;
@@ -806,7 +814,7 @@ dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
 	}
 
 	result = frombuffer(name, alg, flags, proto, rdclass, source, mctx,
-			    &key);
+			    no_rdata, &key);
 	if (result != ISC_R_SUCCESS) {
 		return (result);
 	}
@@ -827,7 +835,7 @@ dst_key_frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
 	REQUIRE(dst_initialized);
 
 	result = frombuffer(name, alg, flags, protocol, rdclass, source, mctx,
-			    &key);
+			    false, &key);
 	if (result != ISC_R_SUCCESS) {
 		return (result);
 	}
@@ -2331,7 +2339,8 @@ computeid(dst_key_t *key) {
 static isc_result_t
 frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
 	   unsigned int protocol, dns_rdataclass_t rdclass,
-	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) {
+	   isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
+	   dst_key_t **keyp) {
 	dst_key_t *key;
 	isc_result_t ret;
 
@@ -2356,10 +2365,12 @@ frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
 			return (DST_R_UNSUPPORTEDALG);
 		}
 
-		ret = key->func->fromdns(key, source);
-		if (ret != ISC_R_SUCCESS) {
-			dst_key_free(&key);
-			return (ret);
+		if (!no_rdata) {
+			ret = key->func->fromdns(key, source);
+			if (ret != ISC_R_SUCCESS) {
+				dst_key_free(&key);
+				return (ret);
+			}
 		}
 	}
 
diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h
index 4744014..73ac1cd 100644
--- a/lib/dns/include/dns/validator.h
+++ b/lib/dns/include/dns/validator.h
@@ -147,6 +147,7 @@ struct dns_validator {
 	unsigned int  depth;
 	unsigned int  authcount;
 	unsigned int  authfail;
+	bool	      failed;
 	isc_stdtime_t start;
 };
 
diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h
index df48d8e..46fb193 100644
--- a/lib/dns/include/dst/dst.h
+++ b/lib/dns/include/dst/dst.h
@@ -469,6 +469,10 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory);
  */
 
 isc_result_t
+dst_key_fromdns_ex(const dns_name_t *name, dns_rdataclass_t rdclass,
+		   isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata,
+		   dst_key_t **keyp);
+isc_result_t
 dst_key_fromdns(const dns_name_t *name, dns_rdataclass_t rdclass,
 		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
 /*%<
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index 5f31d5b..cc8c9ab 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -10559,8 +10559,8 @@ dns_resolver_create(dns_view_t *view, isc_taskmgr_t *taskmgr,
 		 * Since we have a pool of tasks we bind them to task queues
 		 * to spread the load evenly
 		 */
-		result = isc_task_create_bound(taskmgr, 0,
-					       &res->buckets[i].task, i);
+		result = isc_task_create_bound(
+			taskmgr, 0, &res->buckets[i].task, ISC_NM_TASK_SLOW(i));
 		if (result != ISC_R_SUCCESS) {
 			isc_mutex_destroy(&res->buckets[i].lock);
 			goto cleanup_buckets;
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
index e54fc70..e416cc9 100644
--- a/lib/dns/validator.c
+++ b/lib/dns/validator.c
@@ -1098,8 +1098,8 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
  * 'rdataset'.  If found, build a dst_key_t for it and point val->key at
  * it.
  *
- * If val->key is already non-NULL, locate it in the rdataset and then
- * search past it for the *next* key that could have signed 'siginfo', then
+ * If val->key is already non-NULL, start searching from the next position in
+ * 'rdataset' to find the *next* key that could have signed 'siginfo', then
  * set val->key to that.
  *
  * Returns ISC_R_SUCCESS if a possible matching key has been found,
@@ -1112,59 +1112,59 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) {
 	isc_buffer_t b;
 	dns_rdata_t rdata = DNS_RDATA_INIT;
 	dst_key_t *oldkey = val->key;
-	bool foundold;
+	bool no_rdata = false;
 
 	if (oldkey == NULL) {
-		foundold = true;
+		result = dns_rdataset_first(rdataset);
 	} else {
-		foundold = false;
+		dst_key_free(&oldkey);
 		val->key = NULL;
+		result = dns_rdataset_next(rdataset);
 	}
-
-	result = dns_rdataset_first(rdataset);
 	if (result != ISC_R_SUCCESS) {
-		goto failure;
+		goto done;
 	}
+
 	do {
 		dns_rdataset_current(rdataset, &rdata);
 
 		isc_buffer_init(&b, rdata.data, rdata.length);
 		isc_buffer_add(&b, rdata.length);
 		INSIST(val->key == NULL);
-		result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b,
-					 val->view->mctx, &val->key);
+		result = dst_key_fromdns_ex(&siginfo->signer, rdata.rdclass, &b,
+					    val->view->mctx, no_rdata,
+					    &val->key);
 		if (result == ISC_R_SUCCESS) {
 			if (siginfo->algorithm ==
 				    (dns_secalg_t)dst_key_alg(val->key) &&
 			    siginfo->keyid ==
 				    (dns_keytag_t)dst_key_id(val->key) &&
+			    (dst_key_flags(val->key) & DNS_KEYFLAG_REVOKE) ==
+				    0 &&
 			    dst_key_iszonekey(val->key))
 			{
-				if (foundold) {
-					/*
-					 * This is the key we're looking for.
-					 */
-					return (ISC_R_SUCCESS);
-				} else if (dst_key_compare(oldkey, val->key)) {
-					foundold = true;
-					dst_key_free(&oldkey);
+				if (no_rdata) {
+					/* Retry with full key */
+					dns_rdata_reset(&rdata);
+					dst_key_free(&val->key);
+					no_rdata = false;
+					continue;
 				}
+				/* This is the key we're looking for. */
+				goto done;
 			}
 			dst_key_free(&val->key);
 		}
 		dns_rdata_reset(&rdata);
 		result = dns_rdataset_next(rdataset);
+		no_rdata = true;
 	} while (result == ISC_R_SUCCESS);
 
+done:
 	if (result == ISC_R_NOMORE) {
 		result = ISC_R_NOTFOUND;
 	}
 
-failure:
-	if (oldkey != NULL) {
-		dst_key_free(&oldkey);
-	}
-
 	return (result);
 }
 
@@ -1557,20 +1557,9 @@ validate_answer(dns_validator_t *val, bool resume) {
 			continue;
 		}
 
-		do {
-			isc_result_t tresult;
-			vresult = verify(val, val->key, &rdata,
-					 val->siginfo->keyid);
-			if (vresult == ISC_R_SUCCESS) {
-				break;
-			}
-
-			tresult = select_signing_key(val, val->keyset);
-			if (tresult != ISC_R_SUCCESS) {
-				break;
-			}
-		} while (1);
+		vresult = verify(val, val->key, &rdata, val->siginfo->keyid);
 		if (vresult != ISC_R_SUCCESS) {
+			val->failed = true;
 			validator_log(val, ISC_LOG_DEBUG(3),
 				      "failed to verify rdataset");
 		} else {
@@ -1607,9 +1596,13 @@ validate_answer(dns_validator_t *val, bool resume) {
 		} else {
 			validator_log(val, ISC_LOG_DEBUG(3),
 				      "verify failure: %s",
-				      isc_result_totext(result));
+				      isc_result_totext(vresult));
 			resume = false;
 		}
+		if (val->failed) {
+			result = ISC_R_NOMORE;
+			break;
+		}
 	}
 	if (result != ISC_R_NOMORE) {
 		validator_log(val, ISC_LOG_DEBUG(3),
diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h
index dc44ef2..1b88832 100644
--- a/lib/isc/include/isc/netmgr.h
+++ b/lib/isc/include/isc/netmgr.h
@@ -455,6 +455,9 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
  * 'cb'.
  */
 
+#define ISC_NM_TASK_SLOW_OFFSET -2
+#define ISC_NM_TASK_SLOW(i)	(ISC_NM_TASK_SLOW_OFFSET - 1 - i)
+
 void
 isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid);
 /*%<
diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h
index 23bc2a2..80de758 100644
--- a/lib/isc/netmgr/netmgr-int.h
+++ b/lib/isc/netmgr/netmgr-int.h
@@ -630,6 +630,7 @@ struct isc_nm {
 	isc_refcount_t references;
 	isc_mem_t *mctx;
 	int nworkers;
+	int nlisteners;
 	isc_mutex_t lock;
 	isc_condition_t wkstatecond;
 	isc_condition_t wkpausecond;
diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c
index 71c6d62..1aa249e 100644
--- a/lib/isc/netmgr/netmgr.c
+++ b/lib/isc/netmgr/netmgr.c
@@ -272,31 +272,34 @@ isc__nm_winsock_destroy(void) {
 #endif /* WIN32 */
 
 static void
-isc__nm_threadpool_initialize(uint32_t workers) {
+isc__nm_threadpool_initialize(uint32_t nworkers) {
 	char buf[11];
 	int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf,
 			     &(size_t){ sizeof(buf) });
 	if (r == UV_ENOENT) {
-		snprintf(buf, sizeof(buf), "%" PRIu32, workers);
+		snprintf(buf, sizeof(buf), "%" PRIu32, nworkers);
 		uv_os_setenv("UV_THREADPOOL_SIZE", buf);
 	}
 }
 
 void
-isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
+isc__netmgr_create(isc_mem_t *mctx, uint32_t nworkers, isc_nm_t **netmgrp) {
 	isc_nm_t *mgr = NULL;
 	char name[32];
 
-	REQUIRE(workers > 0);
+	REQUIRE(nworkers > 0);
 
 #ifdef WIN32
 	isc__nm_winsock_initialize();
 #endif /* WIN32 */
 
-	isc__nm_threadpool_initialize(workers);
+	isc__nm_threadpool_initialize(nworkers);
 
 	mgr = isc_mem_get(mctx, sizeof(*mgr));
-	*mgr = (isc_nm_t){ .nworkers = workers };
+	*mgr = (isc_nm_t){
+		.nworkers = nworkers * 2,
+		.nlisteners = nworkers,
+	};
 
 	isc_mem_attach(mctx, &mgr->mctx);
 	isc_mutex_init(&mgr->lock);
@@ -337,11 +340,12 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
 	isc_mempool_associatelock(mgr->evpool, &mgr->evlock);
 	isc_mempool_setfillcount(mgr->evpool, 32);
 
-	isc_barrier_init(&mgr->pausing, workers);
-	isc_barrier_init(&mgr->resuming, workers);
+	isc_barrier_init(&mgr->pausing, mgr->nworkers);
+	isc_barrier_init(&mgr->resuming, mgr->nworkers);
 
-	mgr->workers = isc_mem_get(mctx, workers * sizeof(isc__networker_t));
-	for (size_t i = 0; i < workers; i++) {
+        mgr->workers = isc_mem_get(mctx,
+                                   mgr->nworkers * sizeof(isc__networker_t));
+        for (int i = 0; i < mgr->nworkers; i++) {
 		int r;
 		isc__networker_t *worker = &mgr->workers[i];
 		*worker = (isc__networker_t){
@@ -376,7 +380,7 @@ isc__netmgr_create(isc_mem_t *mctx, uint32_t workers, isc_nm_t **netmgrp) {
 		mgr->workers_running++;
 		isc_thread_create(nm_thread, &mgr->workers[i], &worker->thread);
 
-		snprintf(name, sizeof(name), "isc-net-%04zu", i);
+		snprintf(name, sizeof(name), "isc-net-%04d", i);
 		isc_thread_setname(worker->thread, name);
 	}
 
@@ -860,9 +864,15 @@ isc_nm_task_enqueue(isc_nm_t *nm, isc_task_t *task, int threadid) {
 	isc__networker_t *worker = NULL;
 
 	if (threadid == -1) {
-		tid = (int)isc_random_uniform(nm->nworkers);
+		tid = (int)isc_random_uniform(nm->nlisteners);
+	} else if (threadid == ISC_NM_TASK_SLOW_OFFSET) {
+		tid = nm->nlisteners +
+		      (int)isc_random_uniform(nm->nworkers - nm->nlisteners);
+	} else if (threadid < ISC_NM_TASK_SLOW_OFFSET) {
+		tid = nm->nlisteners + (ISC_NM_TASK_SLOW(threadid) %
+					(nm->nworkers - nm->nlisteners));
 	} else {
-		tid = threadid % nm->nworkers;
+		tid = threadid % nm->nlisteners;
 	}
 
 	worker = &nm->workers[tid];
diff --git a/lib/isc/netmgr/tcp.c b/lib/isc/netmgr/tcp.c
index dde592d..c2e059a 100644
--- a/lib/isc/netmgr/tcp.c
+++ b/lib/isc/netmgr/tcp.c
@@ -321,7 +321,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
 			isc__nm_connectcb(sock, req, result, false);
 		} else {
 			isc__nmsocket_clearcb(sock);
-			sock->tid = isc_random_uniform(mgr->nworkers);
+			sock->tid = isc_random_uniform(mgr->nlisteners);
 			isc__nm_connectcb(sock, req, result, true);
 		}
 		atomic_store(&sock->closed, true);
@@ -339,7 +339,7 @@ isc_nm_tcpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
 		isc__nm_put_netievent_tcpconnect(mgr, ievent);
 	} else {
 		atomic_init(&sock->active, false);
-		sock->tid = isc_random_uniform(mgr->nworkers);
+		sock->tid = isc_random_uniform(mgr->nlisteners);
 		isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
 				       (isc__netievent_t *)ievent);
 	}
@@ -435,7 +435,7 @@ isc_nm_listentcp(isc_nm_t *mgr, isc_sockaddr_t *iface,
 #if defined(WIN32)
 	sock->nchildren = 1;
 #else
-	sock->nchildren = mgr->nworkers;
+	sock->nchildren = mgr->nlisteners;
 #endif
 	children_size = sock->nchildren * sizeof(sock->children[0]);
 	sock->children = isc_mem_get(mgr->mctx, children_size);
diff --git a/lib/isc/netmgr/tcpdns.c b/lib/isc/netmgr/tcpdns.c
index f66944e..8f79a7f 100644
--- a/lib/isc/netmgr/tcpdns.c
+++ b/lib/isc/netmgr/tcpdns.c
@@ -301,7 +301,7 @@ isc_nm_tcpdnsconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
 		isc__nm_put_netievent_tcpdnsconnect(mgr, ievent);
 	} else {
 		atomic_init(&sock->active, false);
-		sock->tid = isc_random_uniform(mgr->nworkers);
+		sock->tid = isc_random_uniform(mgr->nlisteners);
 		isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
 				       (isc__netievent_t *)ievent);
 	}
@@ -400,7 +400,7 @@ isc_nm_listentcpdns(isc_nm_t *mgr, isc_sockaddr_t *iface,
 #if defined(WIN32)
 	sock->nchildren = 1;
 #else
-	sock->nchildren = mgr->nworkers;
+	sock->nchildren = mgr->nlisteners;
 #endif
 	children_size = sock->nchildren * sizeof(sock->children[0]);
 	sock->children = isc_mem_get(mgr->mctx, children_size);
diff --git a/lib/isc/netmgr/udp.c b/lib/isc/netmgr/udp.c
index 1af63af..668edca 100644
--- a/lib/isc/netmgr/udp.c
+++ b/lib/isc/netmgr/udp.c
@@ -126,7 +126,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
 	uv_os_sock_t fd = -1;
 
 	/*
-	 * We are creating mgr->nworkers duplicated sockets, one
+	 * We are creating mgr->nlisteners duplicated sockets, one
 	 * socket for each worker thread.
 	 */
 	sock = isc_mem_get(mgr->mctx, sizeof(isc_nmsocket_t));
@@ -136,7 +136,7 @@ isc_nm_listenudp(isc_nm_t *mgr, isc_sockaddr_t *iface, isc_nm_recv_cb_t cb,
 #if defined(WIN32)
 	sock->nchildren = 1;
 #else
-	sock->nchildren = mgr->nworkers;
+	sock->nchildren = mgr->nlisteners;
 #endif
 
 	children_size = sock->nchildren * sizeof(sock->children[0]);
@@ -795,7 +795,7 @@ isc_nm_udpconnect(isc_nm_t *mgr, isc_sockaddr_t *local, isc_sockaddr_t *peer,
 		isc__nm_put_netievent_udpconnect(mgr, event);
 	} else {
 		atomic_init(&sock->active, false);
-		sock->tid = isc_random_uniform(mgr->nworkers);
+		sock->tid = isc_random_uniform(mgr->nlisteners);
 		isc__nm_enqueue_ievent(&mgr->workers[sock->tid],
 				       (isc__netievent_t *)event);
 	}
diff --git a/lib/isc/tests/netmgr_test.c b/lib/isc/tests/netmgr_test.c
index e44909e..d2c19e8 100644
--- a/lib/isc/tests/netmgr_test.c
+++ b/lib/isc/tests/netmgr_test.c
@@ -204,8 +204,6 @@ _setup(void **state __attribute__((unused))) {
 		return (-1);
 	}
 
-	isc_hp_init(4 * workers);
-
 	signal(SIGPIPE, SIG_IGN);
 
 	if (getenv("CI") == NULL || getenv("CI_ENABLE_ALL_TESTS") != NULL) {
-- 
2.33.0

