Blame SOURCES/bind-9.11.4-CVE-2023-50387.patch

ee1d55
From b7c66c030242c9271f8f0629b631e5a84a97cbe6 Mon Sep 17 00:00:00 2001
ee1d55
From: Stepan Broz <sbroz@redhat.com>
ee1d55
Date: Tue, 16 Apr 2024 18:48:51 +0200
ee1d55
Subject: [PATCH] Fail the DNSSEC validation on the first failure
ee1d55
ee1d55
Be more strict when encountering DNSSEC validation failures - fail on
ee1d55
the first failure.  This will break domains that have DNSSEC signing
ee1d55
keys with duplicate key ids, but this is something that's much easier
ee1d55
to fix on the authoritative side, so we are just going to be strict
ee1d55
on the resolver side where it is causing performance problems.
ee1d55
ee1d55
(cherry picked from commit 8b7ecba9885e163c07c2dd3e1ceab79b2ba89e34)
ee1d55
ee1d55
Add normal and slow task queues
ee1d55
ee1d55
Split the task manager queues into normal and slow task queues, so we
ee1d55
can move the tasks that blocks processing for a long time (like DNSSEC
ee1d55
validation) into the slow queue which doesn't block fast
ee1d55
operations (like responding from the cache).  This mitigates the whole
ee1d55
class of KeyTrap-like issues.
ee1d55
ee1d55
(cherry picked from commit db083a21726300916fa0b9fd8a433a796fedf636)
ee1d55
ee1d55
Improve the selecting of the new signing key by remembering where
ee1d55
we stopped the iteration and just continue from that place instead
ee1d55
of iterating from the start over and over again each time.
ee1d55
ee1d55
(cherry picked from commit 75faeefcab47e4f1e12b358525190b4be90f97de)
ee1d55
ee1d55
Optimize selecting the signing key
ee1d55
ee1d55
Don't parse the crypto data before parsing and matching the id and the
ee1d55
algorithm.
ee1d55
ee1d55
(cherry picked from commit b38552cca7200a72658e482f8407f57516efc5db)
ee1d55
ee1d55
6322.   [security]      Specific DNS answers could cause a denial-of-service
ee1d55
                        condition due to DNS validation taking a long time.
ee1d55
                        (CVE-2023-50387) [GL #4424]
ee1d55
ee1d55
                        The same code change also addresses another problem:
ee1d55
                        preparing NSEC3 closest encloser proofs could exhaust
ee1d55
                        available CPU resources. (CVE-2023-50868) [GL #4459]
ee1d55
ee1d55
Related to [GL #4424] and [GL #4459]:
ee1d55
ee1d55
Add normal task queue also to non-thread version
ee1d55
ee1d55
Non-thread builds are used by us for dhcp package. Make it working
ee1d55
again.
ee1d55
---
ee1d55
 lib/dns/dst_api.c               |  25 +++--
ee1d55
 lib/dns/include/dns/validator.h |   1 +
ee1d55
 lib/dns/include/dst/dst.h       |   4 +
ee1d55
 lib/dns/resolver.c              |   2 +-
ee1d55
 lib/dns/validator.c             |  96 ++++++++----------
ee1d55
 lib/dns/win32/libdns.def.in     |   1 +
ee1d55
 lib/isc/include/isc/task.h      |  11 ++-
ee1d55
 lib/isc/task.c                  | 167 ++++++++++++++++++++++----------
ee1d55
 8 files changed, 193 insertions(+), 114 deletions(-)
ee1d55
ee1d55
diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c
ee1d55
index dbece0a..3ed8f7f 100644
ee1d55
--- a/lib/dns/dst_api.c
ee1d55
+++ b/lib/dns/dst_api.c
ee1d55
@@ -103,6 +103,7 @@ static isc_result_t	frombuffer(dns_name_t *name,
ee1d55
 				   dns_rdataclass_t rdclass,
ee1d55
 				   isc_buffer_t *source,
ee1d55
 				   isc_mem_t *mctx,
ee1d55
+				   isc_boolean_t no_rdata,
ee1d55
 				   dst_key_t **keyp);
ee1d55
 
ee1d55
 static isc_result_t	algorithm_status(unsigned int alg);
ee1d55
@@ -751,6 +752,13 @@ isc_result_t
ee1d55
 dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
 		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
ee1d55
 {
ee1d55
+	return (dst_key_fromdns_ex(name, rdclass, source, mctx, ISC_FALSE, keyp));
ee1d55
+}
ee1d55
+
ee1d55
+isc_result_t
ee1d55
+dst_key_fromdns_ex(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
+		   isc_buffer_t *source, isc_mem_t *mctx, isc_boolean_t no_rdata,
ee1d55
+		   dst_key_t **keyp) {
ee1d55
 	isc_uint8_t alg, proto;
ee1d55
 	isc_uint32_t flags, extflags;
ee1d55
 	dst_key_t *key = NULL;
ee1d55
@@ -779,7 +787,7 @@ dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
 	}
ee1d55
 
ee1d55
 	result = frombuffer(name, alg, flags, proto, rdclass, source,
ee1d55
-			    mctx, &key);
ee1d55
+			    mctx, no_rdata, &key);
ee1d55
 	if (result != ISC_R_SUCCESS)
ee1d55
 		return (result);
ee1d55
 	key->key_id = id;
ee1d55
@@ -801,7 +809,7 @@ dst_key_frombuffer(dns_name_t *name, unsigned int alg,
ee1d55
 	REQUIRE(dst_initialized);
ee1d55
 
ee1d55
 	result = frombuffer(name, alg, flags, protocol, rdclass, source,
ee1d55
-			    mctx, &key);
ee1d55
+			    mctx, ISC_FALSE, &key);
ee1d55
 	if (result != ISC_R_SUCCESS)
ee1d55
 		return (result);
ee1d55
 
ee1d55
@@ -1899,7 +1907,8 @@ computeid(dst_key_t *key) {
ee1d55
 static isc_result_t
ee1d55
 frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
ee1d55
 	   unsigned int protocol, dns_rdataclass_t rdclass,
ee1d55
-	   isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp)
ee1d55
+	   isc_buffer_t *source, isc_mem_t *mctx, isc_boolean_t no_rdata,
ee1d55
+	   dst_key_t **keyp)
ee1d55
 {
ee1d55
 	dst_key_t *key;
ee1d55
 	isc_result_t ret;
ee1d55
@@ -1924,10 +1933,12 @@ frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags,
ee1d55
 			return (DST_R_UNSUPPORTEDALG);
ee1d55
 		}
ee1d55
 
ee1d55
-		ret = key->func->fromdns(key, source);
ee1d55
-		if (ret != ISC_R_SUCCESS) {
ee1d55
-			dst_key_free(&key);
ee1d55
-			return (ret);
ee1d55
+		if (!no_rdata) {
ee1d55
+			ret = key->func->fromdns(key, source);
ee1d55
+			if (ret != ISC_R_SUCCESS) {
ee1d55
+				dst_key_free(&key);
ee1d55
+				return (ret);
ee1d55
+			}
ee1d55
 		}
ee1d55
 	}
ee1d55
 
ee1d55
diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h
ee1d55
index 100eab7..a4d15b4 100644
ee1d55
--- a/lib/dns/include/dns/validator.h
ee1d55
+++ b/lib/dns/include/dns/validator.h
ee1d55
@@ -158,6 +158,7 @@ struct dns_validator {
ee1d55
 	unsigned int			depth;
ee1d55
 	unsigned int			authcount;
ee1d55
 	unsigned int			authfail;
ee1d55
+	isc_boolean_t			failed;
ee1d55
 	isc_stdtime_t			start;
ee1d55
 };
ee1d55
 
ee1d55
diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h
ee1d55
index 42c67d4..6018bc2 100644
ee1d55
--- a/lib/dns/include/dst/dst.h
ee1d55
+++ b/lib/dns/include/dst/dst.h
ee1d55
@@ -414,6 +414,10 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory);
ee1d55
  */
ee1d55
 
ee1d55
 isc_result_t
ee1d55
+dst_key_fromdns_ex(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
+		   isc_buffer_t *source, isc_mem_t *mctx, isc_boolean_t no_rdata,
ee1d55
+		   dst_key_t **keyp);
ee1d55
+isc_result_t
ee1d55
 dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
 		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
ee1d55
 /*%<
ee1d55
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
ee1d55
index a55b5fd..b2de13a 100644
ee1d55
--- a/lib/dns/resolver.c
ee1d55
+++ b/lib/dns/resolver.c
ee1d55
@@ -9089,7 +9089,7 @@ dns_resolver_create(dns_view_t *view,
ee1d55
 		if (result != ISC_R_SUCCESS)
ee1d55
 			goto cleanup_buckets;
ee1d55
 		res->buckets[i].task = NULL;
ee1d55
-		result = isc_task_create(taskmgr, 0, &res->buckets[i].task);
ee1d55
+		result = isc_task_create(taskmgr, ISC_TASK_QUANTUM_SLOW, &res->buckets[i].task);
ee1d55
 		if (result != ISC_R_SUCCESS) {
ee1d55
 			DESTROYLOCK(&res->buckets[i].lock);
ee1d55
 			goto cleanup_buckets;
ee1d55
diff --git a/lib/dns/validator.c b/lib/dns/validator.c
ee1d55
index 289b505..695a5c2 100644
ee1d55
--- a/lib/dns/validator.c
ee1d55
+++ b/lib/dns/validator.c
ee1d55
@@ -1197,6 +1197,12 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
ee1d55
  * val->key at it.
ee1d55
  *
ee1d55
  * If val->key is non-NULL, this returns the next matching key.
ee1d55
+ * If val->key is already non-NULL, start searching from the next position in
ee1d55
+ * 'rdataset' to find the *next* key that could have signed 'siginfo', then
ee1d55
+ * set val->key to that.
ee1d55
+ *
ee1d55
+ * Returns ISC_R_SUCCESS if a possible matching key has been found,
ee1d55
+ * ISC_R_NOTFOUND if not. Any other value indicates error.
ee1d55
  */
ee1d55
 static isc_result_t
ee1d55
 get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
ee1d55
@@ -1206,54 +1212,58 @@ get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
ee1d55
 	isc_buffer_t b;
ee1d55
 	dns_rdata_t rdata = DNS_RDATA_INIT;
ee1d55
 	dst_key_t *oldkey = val->key;
ee1d55
-	isc_boolean_t foundold;
ee1d55
+	isc_boolean_t no_rdata = ISC_FALSE;
ee1d55
 
ee1d55
-	if (oldkey == NULL)
ee1d55
-		foundold = ISC_TRUE;
ee1d55
-	else {
ee1d55
-		foundold = ISC_FALSE;
ee1d55
+	if (oldkey == NULL) {
ee1d55
+		result = dns_rdataset_first(rdataset);
ee1d55
+	} else {
ee1d55
+		dst_key_free(&oldkey);
ee1d55
 		val->key = NULL;
ee1d55
+		result = dns_rdataset_next(rdataset);
ee1d55
+	}
ee1d55
+
ee1d55
+	if (result != ISC_R_SUCCESS) {
ee1d55
+		goto done;
ee1d55
 	}
ee1d55
 
ee1d55
-	result = dns_rdataset_first(rdataset);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto failure;
ee1d55
 	do {
ee1d55
 		dns_rdataset_current(rdataset, &rdata);
ee1d55
 
ee1d55
 		isc_buffer_init(&b, rdata.data, rdata.length);
ee1d55
 		isc_buffer_add(&b, rdata.length);
ee1d55
 		INSIST(val->key == NULL);
ee1d55
-		result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b,
ee1d55
-					 val->view->mctx, &val->key);
ee1d55
+		result = dst_key_fromdns_ex(&siginfo->signer, rdata.rdclass, &b,
ee1d55
+					    val->view->mctx, no_rdata,
ee1d55
+					    &val->key);
ee1d55
 		if (result == ISC_R_SUCCESS) {
ee1d55
 			if (siginfo->algorithm ==
ee1d55
 				    (dns_secalg_t)dst_key_alg(val->key) &&
ee1d55
 			    siginfo->keyid ==
ee1d55
 				    (dns_keytag_t)dst_key_id(val->key) &&
ee1d55
+			    (dst_key_flags(val->key) & DNS_KEYFLAG_REVOKE) ==
ee1d55
+				    0 &&
ee1d55
 			    dst_key_iszonekey(val->key))
ee1d55
 			{
ee1d55
-				if (foundold) {
ee1d55
-					/*
ee1d55
-					 * This is the key we're looking for.
ee1d55
-					 */
ee1d55
-					return (ISC_R_SUCCESS);
ee1d55
-				} else if (dst_key_compare(oldkey, val->key)) {
ee1d55
-					foundold = ISC_TRUE;
ee1d55
-					dst_key_free(&oldkey);
ee1d55
+				if (no_rdata) {
ee1d55
+					/* Retry with full key */
ee1d55
+					dns_rdata_reset(&rdata);
ee1d55
+					dst_key_free(&val->key);
ee1d55
+					no_rdata = ISC_FALSE;
ee1d55
+					continue;
ee1d55
 				}
ee1d55
+				/* This is the key we're looking for. */
ee1d55
+				goto done;
ee1d55
 			}
ee1d55
 			dst_key_free(&val->key);
ee1d55
 		}
ee1d55
 		dns_rdata_reset(&rdata);
ee1d55
 		result = dns_rdataset_next(rdataset);
ee1d55
+		no_rdata = ISC_TRUE;
ee1d55
 	} while (result == ISC_R_SUCCESS);
ee1d55
-	if (result == ISC_R_NOMORE)
ee1d55
+done:
ee1d55
+	if (result == ISC_R_NOMORE) {
ee1d55
 		result = ISC_R_NOTFOUND;
ee1d55
-
ee1d55
- failure:
ee1d55
-	if (oldkey != NULL)
ee1d55
-		dst_key_free(&oldkey);
ee1d55
+	}
ee1d55
 
ee1d55
 	return (result);
ee1d55
 }
ee1d55
@@ -1622,37 +1632,13 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
ee1d55
 			continue;
ee1d55
 		}
ee1d55
 
ee1d55
-		do {
ee1d55
-			vresult = verify(val, val->key, &rdata,
ee1d55
-					val->siginfo->keyid);
ee1d55
-			if (vresult == ISC_R_SUCCESS)
ee1d55
-				break;
ee1d55
-			if (val->keynode != NULL) {
ee1d55
-				dns_keynode_t *nextnode = NULL;
ee1d55
-				result = dns_keytable_findnextkeynode(
ee1d55
-							val->keytable,
ee1d55
-							val->keynode,
ee1d55
-							&nextnode);
ee1d55
-				dns_keytable_detachkeynode(val->keytable,
ee1d55
-							   &val->keynode);
ee1d55
-				val->keynode = nextnode;
ee1d55
-				if (result != ISC_R_SUCCESS) {
ee1d55
-					val->key = NULL;
ee1d55
-					break;
ee1d55
-				}
ee1d55
-				val->key = dns_keynode_key(val->keynode);
ee1d55
-				if (val->key == NULL)
ee1d55
-					break;
ee1d55
-			} else {
ee1d55
-				if (get_dst_key(val, val->siginfo, val->keyset)
ee1d55
-				    != ISC_R_SUCCESS)
ee1d55
-					break;
ee1d55
-			}
ee1d55
-		} while (1);
ee1d55
-		if (vresult != ISC_R_SUCCESS)
ee1d55
+		vresult = verify(val, val->key, &rdata,
ee1d55
+				val->siginfo->keyid);
ee1d55
+		if (vresult != ISC_R_SUCCESS) {
ee1d55
+			val->failed = ISC_TRUE;
ee1d55
 			validator_log(val, ISC_LOG_DEBUG(3),
ee1d55
 				      "failed to verify rdataset");
ee1d55
-		else {
ee1d55
+		} else {
ee1d55
 			dns_rdataset_trimttl(event->rdataset,
ee1d55
 					     event->sigrdataset,
ee1d55
 					     val->siginfo, val->start,
ee1d55
@@ -1689,9 +1675,13 @@ validate(dns_validator_t *val, isc_boolean_t resume) {
ee1d55
 		} else {
ee1d55
 			validator_log(val, ISC_LOG_DEBUG(3),
ee1d55
 				      "verify failure: %s",
ee1d55
-				      isc_result_totext(result));
ee1d55
+				      isc_result_totext(vresult));
ee1d55
 			resume = ISC_FALSE;
ee1d55
 		}
ee1d55
+		if (val->failed) {
ee1d55
+			result = ISC_R_NOMORE;
ee1d55
+			break;
ee1d55
+		}
ee1d55
 	}
ee1d55
 	if (result != ISC_R_NOMORE) {
ee1d55
 		validator_log(val, ISC_LOG_DEBUG(3),
ee1d55
diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in
ee1d55
index d48eeb2..54e11a3 100644
ee1d55
--- a/lib/dns/win32/libdns.def.in
ee1d55
+++ b/lib/dns/win32/libdns.def.in
ee1d55
@@ -1432,6 +1432,7 @@ dst_key_format
ee1d55
 dst_key_free
ee1d55
 dst_key_frombuffer
ee1d55
 dst_key_fromdns
ee1d55
+dst_key_fromdns_ex
ee1d55
 dst_key_fromfile
ee1d55
 dst_key_fromgssapi
ee1d55
 dst_key_fromlabel
ee1d55
diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h
ee1d55
index 1273214..9b6f58a 100644
ee1d55
--- a/lib/isc/include/isc/task.h
ee1d55
+++ b/lib/isc/include/isc/task.h
ee1d55
@@ -96,8 +96,15 @@ ISC_LANG_BEGINDECLS
ee1d55
  ***/
ee1d55
 
ee1d55
 typedef enum {
ee1d55
-		isc_taskmgrmode_normal = 0,
ee1d55
-		isc_taskmgrmode_privileged
ee1d55
+	isc_taskqueue_normal = 0,
ee1d55
+	isc_taskqueue_slow = 1,
ee1d55
+} isc_taskqueue_t;
ee1d55
+
ee1d55
+#define ISC_TASK_QUANTUM_SLOW 1024
ee1d55
+
ee1d55
+typedef enum {
ee1d55
+	isc_taskmgrmode_normal = 0,
ee1d55
+	isc_taskmgrmode_privileged
ee1d55
 } isc_taskmgrmode_t;
ee1d55
 
ee1d55
 /*% Task and task manager methods */
ee1d55
diff --git a/lib/isc/task.c b/lib/isc/task.c
ee1d55
index 139ff22..3fe442d 100644
ee1d55
--- a/lib/isc/task.c
ee1d55
+++ b/lib/isc/task.c
ee1d55
@@ -105,6 +105,7 @@ struct isc__task {
ee1d55
 	isc_eventlist_t			on_shutdown;
ee1d55
 	unsigned int			nevents;
ee1d55
 	unsigned int			quantum;
ee1d55
+	unsigned int			qid;
ee1d55
 	unsigned int			flags;
ee1d55
 	isc_stdtime_t			now;
ee1d55
 	isc_time_t			tnow;
ee1d55
@@ -139,11 +140,11 @@ struct isc__taskmgr {
ee1d55
 	/* Locked by task manager lock. */
ee1d55
 	unsigned int			default_quantum;
ee1d55
 	LIST(isc__task_t)		tasks;
ee1d55
-	isc__tasklist_t			ready_tasks;
ee1d55
-	isc__tasklist_t			ready_priority_tasks;
ee1d55
+	isc__tasklist_t			ready_tasks[2];
ee1d55
+	isc__tasklist_t			ready_priority_tasks[2];
ee1d55
 	isc_taskmgrmode_t		mode;
ee1d55
 #ifdef ISC_PLATFORM_USETHREADS
ee1d55
-	isc_condition_t			work_available;
ee1d55
+	isc_condition_t			work_available[2];
ee1d55
 	isc_condition_t			exclusive_granted;
ee1d55
 	isc_condition_t			paused;
ee1d55
 #endif /* ISC_PLATFORM_USETHREADS */
ee1d55
@@ -245,13 +246,13 @@ isc_taskmgrmode_t
ee1d55
 isc__taskmgr_mode(isc_taskmgr_t *manager0);
ee1d55
 
ee1d55
 static inline isc_boolean_t
ee1d55
-empty_readyq(isc__taskmgr_t *manager);
ee1d55
+empty_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid);
ee1d55
 
ee1d55
 static inline isc__task_t *
ee1d55
-pop_readyq(isc__taskmgr_t *manager);
ee1d55
+pop_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid);
ee1d55
 
ee1d55
 static inline void
ee1d55
-push_readyq(isc__taskmgr_t *manager, isc__task_t *task);
ee1d55
+push_readyq(isc__taskmgr_t *manager, isc__task_t *task, isc_taskqueue_t qid);
ee1d55
 
ee1d55
 static struct isc__taskmethods {
ee1d55
 	isc_taskmethods_t methods;
ee1d55
@@ -322,7 +323,8 @@ task_finished(isc__task_t *task) {
ee1d55
 		 * any idle worker threads so they
ee1d55
 		 * can exit.
ee1d55
 		 */
ee1d55
-		BROADCAST(&manager->work_available);
ee1d55
+		BROADCAST(&manager->work_available[isc_taskqueue_normal]);
ee1d55
+		BROADCAST(&manager->work_available[isc_taskqueue_slow]);
ee1d55
 	}
ee1d55
 #endif /* USE_WORKER_THREADS */
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
@@ -360,7 +362,13 @@ isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,
ee1d55
 	INIT_LIST(task->events);
ee1d55
 	INIT_LIST(task->on_shutdown);
ee1d55
 	task->nevents = 0;
ee1d55
-	task->quantum = quantum;
ee1d55
+	if (quantum >= ISC_TASK_QUANTUM_SLOW) {
ee1d55
+		task->qid = isc_taskqueue_slow;
ee1d55
+		task->quantum = quantum - ISC_TASK_QUANTUM_SLOW;
ee1d55
+	} else {
ee1d55
+		task->qid = isc_taskqueue_normal;
ee1d55
+		task->quantum = quantum;
ee1d55
+	}
ee1d55
 	task->flags = 0;
ee1d55
 	task->now = 0;
ee1d55
 	isc_time_settoepoch(&task->tnow);
ee1d55
@@ -471,10 +479,10 @@ task_ready(isc__task_t *task) {
ee1d55
 	XTRACE("task_ready");
ee1d55
 
ee1d55
 	LOCK(&manager->lock);
ee1d55
-	push_readyq(manager, task);
ee1d55
+	push_readyq(manager, task, task->qid);
ee1d55
 #ifdef USE_WORKER_THREADS
ee1d55
 	if (manager->mode == isc_taskmgrmode_normal || has_privilege)
ee1d55
-		SIGNAL(&manager->work_available);
ee1d55
+		SIGNAL(&manager->work_available[task->qid]);
ee1d55
 #endif /* USE_WORKER_THREADS */
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
 }
ee1d55
@@ -945,13 +953,13 @@ isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t) {
ee1d55
  * Caller must hold the task manager lock.
ee1d55
  */
ee1d55
 static inline isc_boolean_t
ee1d55
-empty_readyq(isc__taskmgr_t *manager) {
ee1d55
+empty_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid) {
ee1d55
 	isc__tasklist_t queue;
ee1d55
 
ee1d55
 	if (manager->mode == isc_taskmgrmode_normal)
ee1d55
-		queue = manager->ready_tasks;
ee1d55
+		queue = manager->ready_tasks[qid];
ee1d55
 	else
ee1d55
-		queue = manager->ready_priority_tasks;
ee1d55
+		queue = manager->ready_priority_tasks[qid];
ee1d55
 
ee1d55
 	return (ISC_TF(EMPTY(queue)));
ee1d55
 }
ee1d55
@@ -965,18 +973,18 @@ empty_readyq(isc__taskmgr_t *manager) {
ee1d55
  * Caller must hold the task manager lock.
ee1d55
  */
ee1d55
 static inline isc__task_t *
ee1d55
-pop_readyq(isc__taskmgr_t *manager) {
ee1d55
+pop_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid) {
ee1d55
 	isc__task_t *task;
ee1d55
 
ee1d55
 	if (manager->mode == isc_taskmgrmode_normal)
ee1d55
-		task = HEAD(manager->ready_tasks);
ee1d55
+		task = HEAD(manager->ready_tasks[qid]);
ee1d55
 	else
ee1d55
-		task = HEAD(manager->ready_priority_tasks);
ee1d55
+		task = HEAD(manager->ready_priority_tasks[qid]);
ee1d55
 
ee1d55
 	if (task != NULL) {
ee1d55
-		DEQUEUE(manager->ready_tasks, task, ready_link);
ee1d55
+		DEQUEUE(manager->ready_tasks[qid], task, ready_link);
ee1d55
 		if (ISC_LINK_LINKED(task, ready_priority_link))
ee1d55
-			DEQUEUE(manager->ready_priority_tasks, task,
ee1d55
+			DEQUEUE(manager->ready_priority_tasks[qid], task,
ee1d55
 				ready_priority_link);
ee1d55
 	}
ee1d55
 
ee1d55
@@ -990,16 +998,16 @@ pop_readyq(isc__taskmgr_t *manager) {
ee1d55
  * Caller must hold the task manager lock.
ee1d55
  */
ee1d55
 static inline void
ee1d55
-push_readyq(isc__taskmgr_t *manager, isc__task_t *task) {
ee1d55
-	ENQUEUE(manager->ready_tasks, task, ready_link);
ee1d55
+push_readyq(isc__taskmgr_t *manager, isc__task_t *task, isc_taskqueue_t qid) {
ee1d55
+	ENQUEUE(manager->ready_tasks[qid], task, ready_link);
ee1d55
 	if ((task->flags & TASK_F_PRIVILEGED) != 0)
ee1d55
-		ENQUEUE(manager->ready_priority_tasks, task,
ee1d55
+		ENQUEUE(manager->ready_priority_tasks[qid], task,
ee1d55
 			ready_priority_link);
ee1d55
 	manager->tasks_ready++;
ee1d55
 }
ee1d55
 
ee1d55
 static void
ee1d55
-dispatch(isc__taskmgr_t *manager) {
ee1d55
+dispatch(isc__taskmgr_t *manager, isc_taskqueue_t qid) {
ee1d55
 	isc__task_t *task;
ee1d55
 #ifndef USE_WORKER_THREADS
ee1d55
 	unsigned int total_dispatch_count = 0;
ee1d55
@@ -1078,26 +1086,26 @@ dispatch(isc__taskmgr_t *manager) {
ee1d55
 		 * If a pause has been requested, don't do any work
ee1d55
 		 * until it's been released.
ee1d55
 		 */
ee1d55
-		while ((empty_readyq(manager) || manager->pause_requested ||
ee1d55
+		while ((empty_readyq(manager, qid) || manager->pause_requested ||
ee1d55
 			manager->exclusive_requested) && !FINISHED(manager))
ee1d55
 		{
ee1d55
 			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
ee1d55
 						    ISC_MSGSET_GENERAL,
ee1d55
 						    ISC_MSG_WAIT, "wait"));
ee1d55
-			WAIT(&manager->work_available, &manager->lock);
ee1d55
+			WAIT(&manager->work_available[qid], &manager->lock);
ee1d55
 			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
ee1d55
 						    ISC_MSGSET_TASK,
ee1d55
 						    ISC_MSG_AWAKE, "awake"));
ee1d55
 		}
ee1d55
 #else /* USE_WORKER_THREADS */
ee1d55
 		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
ee1d55
-		    empty_readyq(manager))
ee1d55
+		    empty_readyq(manager, qid))
ee1d55
 			break;
ee1d55
 #endif /* USE_WORKER_THREADS */
ee1d55
 		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
ee1d55
 					    ISC_MSG_WORKING, "working"));
ee1d55
 
ee1d55
-		task = pop_readyq(manager);
ee1d55
+		task = pop_readyq(manager, qid);
ee1d55
 		if (task != NULL) {
ee1d55
 			unsigned int dispatch_count = 0;
ee1d55
 			isc_boolean_t done = ISC_FALSE;
ee1d55
@@ -1261,7 +1269,7 @@ dispatch(isc__taskmgr_t *manager) {
ee1d55
 				 * might even hurt rather than help.
ee1d55
 				 */
ee1d55
 #ifdef USE_WORKER_THREADS
ee1d55
-				push_readyq(manager, task);
ee1d55
+				push_readyq(manager, task, qid);
ee1d55
 #else
ee1d55
 				ENQUEUE(new_ready_tasks, task, ready_link);
ee1d55
 				if ((task->flags & TASK_F_PRIVILEGED) != 0)
ee1d55
@@ -1279,20 +1287,24 @@ dispatch(isc__taskmgr_t *manager) {
ee1d55
 		 * we're stuck.  Automatically drop privileges at that
ee1d55
 		 * point and continue with the regular ready queue.
ee1d55
 		 */
ee1d55
-		if (manager->tasks_running == 0 && empty_readyq(manager)) {
ee1d55
+		if (manager->tasks_running == 0 && empty_readyq(manager, isc_taskqueue_normal) && empty_readyq(manager, isc_taskqueue_slow)) {
ee1d55
 			manager->mode = isc_taskmgrmode_normal;
ee1d55
-			if (!empty_readyq(manager))
ee1d55
-				BROADCAST(&manager->work_available);
ee1d55
+			if (!empty_readyq(manager, isc_taskqueue_normal)) {
ee1d55
+				BROADCAST(&manager->work_available[isc_taskqueue_normal]);
ee1d55
+			}
ee1d55
+			if (!empty_readyq(manager, isc_taskqueue_slow)) {
ee1d55
+				BROADCAST(&manager->work_available[isc_taskqueue_slow]);
ee1d55
+			}
ee1d55
 		}
ee1d55
 #endif
ee1d55
 	}
ee1d55
 
ee1d55
 #ifndef USE_WORKER_THREADS
ee1d55
-	ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link);
ee1d55
-	ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks,
ee1d55
+	ISC_LIST_APPENDLIST(manager->ready_tasks[qid], new_ready_tasks, ready_link);
ee1d55
+	ISC_LIST_APPENDLIST(manager->ready_priority_tasks[qid], new_priority_tasks,
ee1d55
 			    ready_priority_link);
ee1d55
 	manager->tasks_ready += tasks_ready;
ee1d55
-	if (empty_readyq(manager))
ee1d55
+	if (empty_readyq(manager, qid))
ee1d55
 		manager->mode = isc_taskmgrmode_normal;
ee1d55
 #endif
ee1d55
 
ee1d55
@@ -1304,13 +1316,37 @@ static isc_threadresult_t
ee1d55
 #ifdef _WIN32
ee1d55
 WINAPI
ee1d55
 #endif
ee1d55
-run(void *uap) {
ee1d55
+run_normal(void *uap) {
ee1d55
 	isc__taskmgr_t *manager = uap;
ee1d55
 
ee1d55
 	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ee1d55
 				    ISC_MSG_STARTING, "starting"));
ee1d55
 
ee1d55
-	dispatch(manager);
ee1d55
+	dispatch(manager, isc_taskqueue_normal);
ee1d55
+
ee1d55
+	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ee1d55
+				    ISC_MSG_EXITING, "exiting"));
ee1d55
+
ee1d55
+#ifdef OPENSSL_LEAKS
ee1d55
+	ERR_remove_state(0);
ee1d55
+#endif
ee1d55
+
ee1d55
+	return ((isc_threadresult_t)0);
ee1d55
+}
ee1d55
+#endif /* USE_WORKER_THREADS */
ee1d55
+
ee1d55
+#ifdef USE_WORKER_THREADS
ee1d55
+static isc_threadresult_t
ee1d55
+#ifdef _WIN32
ee1d55
+WINAPI
ee1d55
+#endif
ee1d55
+run_slow(void *uap) {
ee1d55
+	isc__taskmgr_t *manager = uap;
ee1d55
+
ee1d55
+	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ee1d55
+				    ISC_MSG_STARTING, "starting"));
ee1d55
+
ee1d55
+	dispatch(manager, isc_taskqueue_slow);
ee1d55
 
ee1d55
 	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ee1d55
 				    ISC_MSG_EXITING, "exiting"));
ee1d55
@@ -1329,7 +1365,8 @@ manager_free(isc__taskmgr_t *manager) {
ee1d55
 
ee1d55
 #ifdef USE_WORKER_THREADS
ee1d55
 	(void)isc_condition_destroy(&manager->exclusive_granted);
ee1d55
-	(void)isc_condition_destroy(&manager->work_available);
ee1d55
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_normal]);
ee1d55
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_slow]);
ee1d55
 	(void)isc_condition_destroy(&manager->paused);
ee1d55
 	isc_mem_free(manager->mctx, manager->threads);
ee1d55
 #endif /* USE_WORKER_THREADS */
ee1d55
@@ -1396,12 +1433,20 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
ee1d55
 #ifdef USE_WORKER_THREADS
ee1d55
 	manager->workers = 0;
ee1d55
 	manager->threads = isc_mem_allocate(mctx,
ee1d55
-					    workers * sizeof(isc_thread_t));
ee1d55
+					    2 * workers * sizeof(isc_thread_t));
ee1d55
 	if (manager->threads == NULL) {
ee1d55
 		result = ISC_R_NOMEMORY;
ee1d55
 		goto cleanup_lock;
ee1d55
 	}
ee1d55
-	if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
ee1d55
+	if (isc_condition_init(&manager->work_available[isc_taskqueue_normal]) != ISC_R_SUCCESS) {
ee1d55
+		UNEXPECTED_ERROR(__FILE__, __LINE__,
ee1d55
+				 "isc_condition_init() %s",
ee1d55
+				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ee1d55
+						ISC_MSG_FAILED, "failed"));
ee1d55
+		result = ISC_R_UNEXPECTED;
ee1d55
+		goto cleanup_threads;
ee1d55
+	}
ee1d55
+	if (isc_condition_init(&manager->work_available[isc_taskqueue_slow]) != ISC_R_SUCCESS) {
ee1d55
 		UNEXPECTED_ERROR(__FILE__, __LINE__,
ee1d55
 				 "isc_condition_init() %s",
ee1d55
 				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ee1d55
@@ -1430,8 +1475,10 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
ee1d55
 		default_quantum = DEFAULT_DEFAULT_QUANTUM;
ee1d55
 	manager->default_quantum = default_quantum;
ee1d55
 	INIT_LIST(manager->tasks);
ee1d55
-	INIT_LIST(manager->ready_tasks);
ee1d55
-	INIT_LIST(manager->ready_priority_tasks);
ee1d55
+	INIT_LIST(manager->ready_tasks[isc_taskqueue_normal]);
ee1d55
+	INIT_LIST(manager->ready_tasks[isc_taskqueue_slow]);
ee1d55
+	INIT_LIST(manager->ready_priority_tasks[isc_taskqueue_normal]);
ee1d55
+	INIT_LIST(manager->ready_priority_tasks[isc_taskqueue_slow]);
ee1d55
 	manager->tasks_running = 0;
ee1d55
 	manager->tasks_ready = 0;
ee1d55
 	manager->exclusive_requested = ISC_FALSE;
ee1d55
@@ -1447,7 +1494,19 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
ee1d55
 	 * Start workers.
ee1d55
 	 */
ee1d55
 	for (i = 0; i < workers; i++) {
ee1d55
-		if (isc_thread_create(run, manager,
ee1d55
+		if (isc_thread_create(run_normal, manager,
ee1d55
+				      &manager->threads[manager->workers]) ==
ee1d55
+		    ISC_R_SUCCESS) {
ee1d55
+			char name[21];	/* thread name limit on Linux */
ee1d55
+			snprintf(name, sizeof(name), "isc-worker%04u", i);
ee1d55
+			isc_thread_setname(manager->threads[manager->workers],
ee1d55
+					   name);
ee1d55
+			manager->workers++;
ee1d55
+			started++;
ee1d55
+		}
ee1d55
+	}
ee1d55
+	for (; i < workers * 2; i++) {
ee1d55
+		if (isc_thread_create(run_slow, manager,
ee1d55
 				      &manager->threads[manager->workers]) ==
ee1d55
 		    ISC_R_SUCCESS) {
ee1d55
 			char name[16];	/* thread name limit on Linux */
ee1d55
@@ -1464,7 +1523,7 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
ee1d55
 		manager_free(manager);
ee1d55
 		return (ISC_R_NOTHREADS);
ee1d55
 	}
ee1d55
-	isc_thread_setconcurrency(workers);
ee1d55
+	isc_thread_setconcurrency(workers * 2);
ee1d55
 #endif /* USE_WORKER_THREADS */
ee1d55
 #ifdef USE_SHARED_MANAGER
ee1d55
 	manager->refs = 1;
ee1d55
@@ -1479,7 +1538,8 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers,
ee1d55
  cleanup_exclusivegranted:
ee1d55
 	(void)isc_condition_destroy(&manager->exclusive_granted);
ee1d55
  cleanup_workavailable:
ee1d55
-	(void)isc_condition_destroy(&manager->work_available);
ee1d55
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_slow]);
ee1d55
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_normal]);
ee1d55
  cleanup_threads:
ee1d55
 	isc_mem_free(mctx, manager->threads);
ee1d55
  cleanup_lock:
ee1d55
@@ -1564,7 +1624,7 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) {
ee1d55
 	     task = NEXT(task, link)) {
ee1d55
 		LOCK(&task->lock);
ee1d55
 		if (task_shutdown(task))
ee1d55
-			push_readyq(manager, task);
ee1d55
+			push_readyq(manager, task, task->qid);
ee1d55
 		UNLOCK(&task->lock);
ee1d55
 	}
ee1d55
 #ifdef USE_WORKER_THREADS
ee1d55
@@ -1573,7 +1633,8 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) {
ee1d55
 	 * there's work left to do, and if there are already no tasks left
ee1d55
 	 * it will cause the workers to see manager->exiting.
ee1d55
 	 */
ee1d55
-	BROADCAST(&manager->work_available);
ee1d55
+	BROADCAST(&manager->work_available[isc_taskqueue_normal]);
ee1d55
+	BROADCAST(&manager->work_available[isc_taskqueue_slow]);
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
 
ee1d55
 	/*
ee1d55
@@ -1634,7 +1695,8 @@ isc__taskmgr_ready(isc_taskmgr_t *manager0) {
ee1d55
 		return (ISC_FALSE);
ee1d55
 
ee1d55
 	LOCK(&manager->lock);
ee1d55
-	is_ready = !empty_readyq(manager);
ee1d55
+	is_ready = !empty_readyq(manager, isc_taskqueue_normal) ||
ee1d55
+		   !empty_readyq(manager, isc_taskqueue_slow);
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
 
ee1d55
 	return (is_ready);
ee1d55
@@ -1651,7 +1713,8 @@ isc__taskmgr_dispatch(isc_taskmgr_t *manager0) {
ee1d55
 	if (manager == NULL)
ee1d55
 		return (ISC_R_NOTFOUND);
ee1d55
 
ee1d55
-	dispatch(manager);
ee1d55
+	dispatch(manager, isc_taskqueue_normal);
ee1d55
+	dispatch(manager, isc_taskqueue_slow);
ee1d55
 
ee1d55
 	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
@@ -1675,7 +1738,8 @@ isc__taskmgr_resume(isc_taskmgr_t *manager0) {
ee1d55
 	LOCK(&manager->lock);
ee1d55
 	if (manager->pause_requested) {
ee1d55
 		manager->pause_requested = ISC_FALSE;
ee1d55
-		BROADCAST(&manager->work_available);
ee1d55
+		BROADCAST(&manager->work_available[isc_taskqueue_normal]);
ee1d55
+		BROADCAST(&manager->work_available[isc_taskqueue_slow]);
ee1d55
 	}
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
 }
ee1d55
@@ -1751,7 +1815,8 @@ isc__task_endexclusive(isc_task_t *task0) {
ee1d55
 	LOCK(&manager->lock);
ee1d55
 	REQUIRE(manager->exclusive_requested);
ee1d55
 	manager->exclusive_requested = ISC_FALSE;
ee1d55
-	BROADCAST(&manager->work_available);
ee1d55
+	BROADCAST(&manager->work_available[isc_taskqueue_normal]);
ee1d55
+	BROADCAST(&manager->work_available[isc_taskqueue_slow]);
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
 #else
ee1d55
 	UNUSED(task0);
ee1d55
@@ -1777,10 +1842,10 @@ isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv) {
ee1d55
 
ee1d55
 	LOCK(&manager->lock);
ee1d55
 	if (priv && ISC_LINK_LINKED(task, ready_link))
ee1d55
-		ENQUEUE(manager->ready_priority_tasks, task,
ee1d55
+		ENQUEUE(manager->ready_priority_tasks[task->qid], task,
ee1d55
 			ready_priority_link);
ee1d55
 	else if (!priv && ISC_LINK_LINKED(task, ready_priority_link))
ee1d55
-		DEQUEUE(manager->ready_priority_tasks, task,
ee1d55
+		DEQUEUE(manager->ready_priority_tasks[task->qid], task,
ee1d55
 			ready_priority_link);
ee1d55
 	UNLOCK(&manager->lock);
ee1d55
 }
ee1d55
-- 
ee1d55
2.44.0
ee1d55