File bind-CVE-2023-50387-CVE-2023-50868.patch of Package bind.34020

From 8b7ecba9885e163c07c2dd3e1ceab79b2ba89e34 Mon Sep 17 00:00:00 2001
From: Mark Andrews <marka@isc.org>
Date: Wed, 22 Nov 2023 16:59:03 +1100
Subject: [PATCH 1/5] Fail the DNSSEC validation on the first failure

Be more strict when encountering DNSSEC validation failures - fail on
the first failure.  This will break domains that have DNSSEC signing
keys with duplicate key ids, but this is something that's much easier
to fix on the authoritative side, so we are just going to be strict
on the resolver side where it is causing performance problems.
---
 lib/dns/include/dns/validator.h |  1 +
 lib/dns/validator.c             | 42 ++++++++++-----------------------
 2 files changed, 13 insertions(+), 30 deletions(-)

Index: bind-9.11.22/lib/dns/include/dns/validator.h
===================================================================
--- bind-9.11.22.orig/lib/dns/include/dns/validator.h
+++ bind-9.11.22/lib/dns/include/dns/validator.h
@@ -160,6 +160,7 @@ struct dns_validator {
 	unsigned int			depth;
 	unsigned int			authcount;
 	unsigned int			authfail;
+	bool				failed;
 	isc_stdtime_t			start;
 };
 
Index: bind-9.11.22/lib/dns/validator.c
===================================================================
--- bind-9.11.22.orig/lib/dns/validator.c
+++ bind-9.11.22/lib/dns/validator.c
@@ -1200,6 +1200,12 @@ create_validator(dns_validator_t *val, d
  * val->key at it.
  *
  * If val->key is non-NULL, this returns the next matching key.
+ * 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,
+ * ISC_R_NOTFOUND if not. Any other value indicates error.
  */
 static isc_result_t
 get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo,
@@ -1209,54 +1215,59 @@ get_dst_key(dns_validator_t *val, dns_rd
 	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;
-	else {
-		foundold = false;
+	if (oldkey == NULL) {
+		result = dns_rdataset_first(rdataset);
+	} else {
+		dst_key_free(&oldkey);
 		val->key = NULL;
+		result = dns_rdataset_next(rdataset);
+	}
+
+	if (result != ISC_R_SUCCESS) {
+		goto done;
 	}
 
-	result = dns_rdataset_first(rdataset);
-	if (result != ISC_R_SUCCESS)
-		goto failure;
 	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);
-	if (result == ISC_R_NOMORE)
-		result = ISC_R_NOTFOUND;
 
- failure:
-	if (oldkey != NULL)
-		dst_key_free(&oldkey);
+done:
+	if (result == ISC_R_NOMORE) {
+		result = ISC_R_NOTFOUND;
+	}
 
 	return (result);
 }
@@ -1626,37 +1637,13 @@ validate(dns_validator_t *val, bool resu
 			continue;
 		}
 
-		do {
-			vresult = verify(val, val->key, &rdata,
-					val->siginfo->keyid);
-			if (vresult == ISC_R_SUCCESS)
-				break;
-			if (val->keynode != NULL) {
-				dns_keynode_t *nextnode = NULL;
-				result = dns_keytable_findnextkeynode(
-							val->keytable,
-							val->keynode,
-							&nextnode);
-				dns_keytable_detachkeynode(val->keytable,
-							   &val->keynode);
-				val->keynode = nextnode;
-				if (result != ISC_R_SUCCESS) {
-					val->key = NULL;
-					break;
-				}
-				val->key = dns_keynode_key(val->keynode);
-				if (val->key == NULL)
-					break;
-			} else {
-				if (get_dst_key(val, val->siginfo, val->keyset)
-				    != ISC_R_SUCCESS)
-					break;
-			}
-		} while (1);
-		if (vresult != ISC_R_SUCCESS)
+		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 {
+		} else {
 			dns_rdataset_trimttl(event->rdataset,
 					     event->sigrdataset,
 					     val->siginfo, val->start,
@@ -1693,9 +1680,13 @@ validate(dns_validator_t *val, bool resu
 		} 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),
Index: bind-9.11.22/lib/dns/resolver.c
===================================================================
--- bind-9.11.22.orig/lib/dns/resolver.c
+++ bind-9.11.22/lib/dns/resolver.c
@@ -9156,7 +9156,7 @@ dns_resolver_create(dns_view_t *view,
 		if (result != ISC_R_SUCCESS)
 			goto cleanup_buckets;
 		res->buckets[i].task = NULL;
-		result = isc_task_create(taskmgr, 0, &res->buckets[i].task);
+		result = isc_task_create(taskmgr, ISC_TASK_QUANTUM_SLOW, &res->buckets[i].task);
 		if (result != ISC_R_SUCCESS) {
 			DESTROYLOCK(&res->buckets[i].lock);
 			goto cleanup_buckets;
Index: bind-9.11.22/lib/isc/include/isc/task.h
===================================================================
--- bind-9.11.22.orig/lib/isc/include/isc/task.h
+++ bind-9.11.22/lib/isc/include/isc/task.h
@@ -98,8 +98,15 @@ ISC_LANG_BEGINDECLS
  ***/
 
 typedef enum {
-		isc_taskmgrmode_normal = 0,
-		isc_taskmgrmode_privileged
+	isc_taskqueue_normal = 0,
+	isc_taskqueue_slow = 1,
+} isc_taskqueue_t;
+
+#define ISC_TASK_QUANTUM_SLOW 1024
+
+typedef enum {
+	isc_taskmgrmode_normal = 0,
+	isc_taskmgrmode_privileged
 } isc_taskmgrmode_t;
 
 /*% Task and task manager methods */
Index: bind-9.11.22/lib/isc/task.c
===================================================================
--- bind-9.11.22.orig/lib/isc/task.c
+++ bind-9.11.22/lib/isc/task.c
@@ -107,6 +107,7 @@ struct isc__task {
 	isc_eventlist_t			on_shutdown;
 	unsigned int			nevents;
 	unsigned int			quantum;
+	unsigned int			qid;
 	unsigned int			flags;
 	isc_stdtime_t			now;
 	isc_time_t			tnow;
@@ -141,11 +142,11 @@ struct isc__taskmgr {
 	/* Locked by task manager lock. */
 	unsigned int			default_quantum;
 	LIST(isc__task_t)		tasks;
-	isc__tasklist_t			ready_tasks;
-	isc__tasklist_t			ready_priority_tasks;
+	isc__tasklist_t			ready_tasks[2];
+	isc__tasklist_t			ready_priority_tasks[2];
 	isc_taskmgrmode_t		mode;
 #ifdef ISC_PLATFORM_USETHREADS
-	isc_condition_t			work_available;
+	isc_condition_t			work_available[2];
 	isc_condition_t			exclusive_granted;
 	isc_condition_t			paused;
 #endif /* ISC_PLATFORM_USETHREADS */
@@ -247,13 +248,13 @@ isc_taskmgrmode_t
 isc__taskmgr_mode(isc_taskmgr_t *manager0);
 
 static inline bool
-empty_readyq(isc__taskmgr_t *manager);
+empty_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid);
 
 static inline isc__task_t *
-pop_readyq(isc__taskmgr_t *manager);
+pop_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid);
 
 static inline void
-push_readyq(isc__taskmgr_t *manager, isc__task_t *task);
+push_readyq(isc__taskmgr_t *manager, isc__task_t *task, isc_taskqueue_t qid);
 
 static struct isc__taskmethods {
 	isc_taskmethods_t methods;
@@ -324,7 +325,8 @@ task_finished(isc__task_t *task) {
 		 * any idle worker threads so they
 		 * can exit.
 		 */
-		BROADCAST(&manager->work_available);
+		BROADCAST(&manager->work_available[isc_taskqueue_normal]);
+		BROADCAST(&manager->work_available[isc_taskqueue_slow]);
 	}
 #endif /* USE_WORKER_THREADS */
 	UNLOCK(&manager->lock);
@@ -364,7 +366,13 @@ isc__task_create(isc_taskmgr_t *manager0
 	INIT_LIST(task->events);
 	INIT_LIST(task->on_shutdown);
 	task->nevents = 0;
-	task->quantum = quantum;
+	if (quantum >= ISC_TASK_QUANTUM_SLOW) {
+		task->qid = isc_taskqueue_slow;
+		task->quantum = quantum - ISC_TASK_QUANTUM_SLOW;
+	} else {
+		task->qid = isc_taskqueue_normal;
+		task->quantum = quantum;
+	}
 	task->flags = 0;
 	task->now = 0;
 	isc_time_settoepoch(&task->tnow);
@@ -476,11 +484,11 @@ task_ready(isc__task_t *task) {
 
 	LOCK(&manager->lock);
 	LOCK(&task->lock);
-	push_readyq(manager, task);
+	push_readyq(manager, task, task->qid);
 	UNLOCK(&task->lock);
 #ifdef USE_WORKER_THREADS
 	if (manager->mode == isc_taskmgrmode_normal || has_privilege)
-		SIGNAL(&manager->work_available);
+		SIGNAL(&manager->work_available[task->qid]);
 #endif /* USE_WORKER_THREADS */
 	UNLOCK(&manager->lock);
 }
@@ -961,13 +969,13 @@ isc__task_getcurrenttimex(isc_task_t *ta
  * Caller must hold the task manager lock.
  */
 static inline bool
-empty_readyq(isc__taskmgr_t *manager) {
+empty_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid) {
 	isc__tasklist_t queue;
 
 	if (manager->mode == isc_taskmgrmode_normal)
-		queue = manager->ready_tasks;
+		queue = manager->ready_tasks[qid];
 	else
-		queue = manager->ready_priority_tasks;
+		queue = manager->ready_priority_tasks[qid];
 
 	return (EMPTY(queue));
 }
@@ -981,18 +989,18 @@ empty_readyq(isc__taskmgr_t *manager) {
  * Caller must hold the task manager lock.
  */
 static inline isc__task_t *
-pop_readyq(isc__taskmgr_t *manager) {
+pop_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid) {
 	isc__task_t *task;
 
 	if (manager->mode == isc_taskmgrmode_normal)
-		task = HEAD(manager->ready_tasks);
+		task = HEAD(manager->ready_tasks[qid]);
 	else
-		task = HEAD(manager->ready_priority_tasks);
+		task = HEAD(manager->ready_priority_tasks[qid]);
 
 	if (task != NULL) {
-		DEQUEUE(manager->ready_tasks, task, ready_link);
+		DEQUEUE(manager->ready_tasks[qid], task, ready_link);
 		if (ISC_LINK_LINKED(task, ready_priority_link))
-			DEQUEUE(manager->ready_priority_tasks, task,
+			DEQUEUE(manager->ready_priority_tasks[qid], task,
 				ready_priority_link);
 	}
 
@@ -1006,16 +1014,16 @@ pop_readyq(isc__taskmgr_t *manager) {
  * Caller must hold the task manager lock.
  */
 static inline void
-push_readyq(isc__taskmgr_t *manager, isc__task_t *task) {
-	ENQUEUE(manager->ready_tasks, task, ready_link);
+push_readyq(isc__taskmgr_t *manager, isc__task_t *task, isc_taskqueue_t qid) {
+	ENQUEUE(manager->ready_tasks[qid], task, ready_link);
 	if ((task->flags & TASK_F_PRIVILEGED) != 0)
-		ENQUEUE(manager->ready_priority_tasks, task,
+		ENQUEUE(manager->ready_priority_tasks[qid], task,
 			ready_priority_link);
 	manager->tasks_ready++;
 }
 
 static void
-dispatch(isc__taskmgr_t *manager) {
+dispatch(isc__taskmgr_t *manager, isc_taskqueue_t qid) {
 	isc__task_t *task;
 #ifndef USE_WORKER_THREADS
 	unsigned int total_dispatch_count = 0;
@@ -1094,26 +1102,26 @@ dispatch(isc__taskmgr_t *manager) {
 		 * If a pause has been requested, don't do any work
 		 * until it's been released.
 		 */
-		while ((empty_readyq(manager) || manager->pause_requested ||
+		while ((empty_readyq(manager, qid) || manager->pause_requested ||
 			manager->exclusive_requested) && !FINISHED(manager))
 		{
 			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
 						    ISC_MSGSET_GENERAL,
 						    ISC_MSG_WAIT, "wait"));
-			WAIT(&manager->work_available, &manager->lock);
+			WAIT(&manager->work_available[qid], &manager->lock);
 			XTHREADTRACE(isc_msgcat_get(isc_msgcat,
 						    ISC_MSGSET_TASK,
 						    ISC_MSG_AWAKE, "awake"));
 		}
 #else /* USE_WORKER_THREADS */
 		if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
-		    empty_readyq(manager))
+		    empty_readyq(manager, qid))
 			break;
 #endif /* USE_WORKER_THREADS */
 		XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
 					    ISC_MSG_WORKING, "working"));
 
-		task = pop_readyq(manager);
+		task = pop_readyq(manager, qid);
 		if (task != NULL) {
 			unsigned int dispatch_count = 0;
 			bool done = false;
@@ -1278,7 +1286,7 @@ dispatch(isc__taskmgr_t *manager) {
 				 */
 #ifdef USE_WORKER_THREADS
 				LOCK(&task->lock);
-				push_readyq(manager, task);
+				push_readyq(manager, task, qid);
 				UNLOCK(&task->lock);
 #else
 				ENQUEUE(new_ready_tasks, task, ready_link);
@@ -1297,20 +1305,24 @@ dispatch(isc__taskmgr_t *manager) {
 		 * we're stuck.  Automatically drop privileges at that
 		 * point and continue with the regular ready queue.
 		 */
-		if (manager->tasks_running == 0 && empty_readyq(manager)) {
+		if (manager->tasks_running == 0 && empty_readyq(manager, isc_taskqueue_normal) && empty_readyq(manager, isc_taskqueue_slow)) {
 			manager->mode = isc_taskmgrmode_normal;
-			if (!empty_readyq(manager))
-				BROADCAST(&manager->work_available);
+			if (!empty_readyq(manager, isc_taskqueue_normal)) {
+				BROADCAST(&manager->work_available[isc_taskqueue_normal]);
+			}
+			if (!empty_readyq(manager, isc_taskqueue_slow)) {
+				BROADCAST(&manager->work_available[isc_taskqueue_slow]);
+			}
 		}
 #endif
 	}
 
 #ifndef USE_WORKER_THREADS
-	ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link);
-	ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks,
+	ISC_LIST_APPENDLIST(manager->ready_tasks[qid], new_ready_tasks, ready_link);
+	ISC_LIST_APPENDLIST(manager->ready_priority_tasks[qid], new_priority_tasks,
 			    ready_priority_link);
 	manager->tasks_ready += tasks_ready;
-	if (empty_readyq(manager))
+	if (empty_readyq(manager, qid))
 		manager->mode = isc_taskmgrmode_normal;
 #endif
 
@@ -1322,13 +1334,37 @@ static isc_threadresult_t
 #ifdef _WIN32
 WINAPI
 #endif
-run(void *uap) {
+run_normal(void *uap) {
+	isc__taskmgr_t *manager = uap;
+
+	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+				    ISC_MSG_STARTING, "starting"));
+
+	dispatch(manager, isc_taskqueue_normal);
+
+	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+				    ISC_MSG_EXITING, "exiting"));
+
+#ifdef OPENSSL_LEAKS
+	ERR_remove_state(0);
+#endif
+
+	return ((isc_threadresult_t)0);
+}
+#endif /* USE_WORKER_THREADS */
+
+#ifdef USE_WORKER_THREADS
+static isc_threadresult_t
+#ifdef _WIN32
+WINAPI
+#endif
+run_slow(void *uap) {
 	isc__taskmgr_t *manager = uap;
 
 	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
 				    ISC_MSG_STARTING, "starting"));
 
-	dispatch(manager);
+	dispatch(manager, isc_taskqueue_slow);
 
 	XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
 				    ISC_MSG_EXITING, "exiting"));
@@ -1347,7 +1383,8 @@ manager_free(isc__taskmgr_t *manager) {
 
 #ifdef USE_WORKER_THREADS
 	(void)isc_condition_destroy(&manager->exclusive_granted);
-	(void)isc_condition_destroy(&manager->work_available);
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_normal]);
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_slow]);
 	(void)isc_condition_destroy(&manager->paused);
 	isc_mem_free(manager->mctx, manager->threads);
 #endif /* USE_WORKER_THREADS */
@@ -1414,12 +1451,20 @@ isc__taskmgr_create(isc_mem_t *mctx, uns
 #ifdef USE_WORKER_THREADS
 	manager->workers = 0;
 	manager->threads = isc_mem_allocate(mctx,
-					    workers * sizeof(isc_thread_t));
+					    2 * workers * sizeof(isc_thread_t));
 	if (manager->threads == NULL) {
 		result = ISC_R_NOMEMORY;
 		goto cleanup_lock;
 	}
-	if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
+	if (isc_condition_init(&manager->work_available[isc_taskqueue_normal]) != ISC_R_SUCCESS) {
+		UNEXPECTED_ERROR(__FILE__, __LINE__,
+				 "isc_condition_init() %s",
+				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+						ISC_MSG_FAILED, "failed"));
+		result = ISC_R_UNEXPECTED;
+		goto cleanup_threads;
+	}
+	if (isc_condition_init(&manager->work_available[isc_taskqueue_slow]) != ISC_R_SUCCESS) {
 		UNEXPECTED_ERROR(__FILE__, __LINE__,
 				 "isc_condition_init() %s",
 				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
@@ -1448,8 +1493,10 @@ isc__taskmgr_create(isc_mem_t *mctx, uns
 		default_quantum = DEFAULT_DEFAULT_QUANTUM;
 	manager->default_quantum = default_quantum;
 	INIT_LIST(manager->tasks);
-	INIT_LIST(manager->ready_tasks);
-	INIT_LIST(manager->ready_priority_tasks);
+	INIT_LIST(manager->ready_tasks[isc_taskqueue_normal]);
+	INIT_LIST(manager->ready_tasks[isc_taskqueue_slow]);
+	INIT_LIST(manager->ready_priority_tasks[isc_taskqueue_normal]);
+	INIT_LIST(manager->ready_priority_tasks[isc_taskqueue_slow]);
 	manager->tasks_running = 0;
 	manager->tasks_ready = 0;
 	manager->exclusive_requested = false;
@@ -1465,7 +1512,19 @@ isc__taskmgr_create(isc_mem_t *mctx, uns
 	 * Start workers.
 	 */
 	for (i = 0; i < workers; i++) {
-		if (isc_thread_create(run, manager,
+		if (isc_thread_create(run_normal, manager,
+				      &manager->threads[manager->workers]) ==
+		    ISC_R_SUCCESS) {
+			char name[21];	/* thread name limit on Linux */
+			snprintf(name, sizeof(name), "isc-worker%04u", i);
+			isc_thread_setname(manager->threads[manager->workers],
+					   name);
+			manager->workers++;
+			started++;
+		}
+	}
+	for (; i < workers * 2; i++) {
+		if (isc_thread_create(run_slow, manager,
 				      &manager->threads[manager->workers]) ==
 		    ISC_R_SUCCESS) {
 			char name[21];	/* thread name limit on Linux */
@@ -1482,7 +1541,7 @@ isc__taskmgr_create(isc_mem_t *mctx, uns
 		manager_free(manager);
 		return (ISC_R_NOTHREADS);
 	}
-	isc_thread_setconcurrency(workers);
+	isc_thread_setconcurrency(workers * 2);
 #endif /* USE_WORKER_THREADS */
 #ifdef USE_SHARED_MANAGER
 	manager->refs = 1;
@@ -1497,7 +1556,8 @@ isc__taskmgr_create(isc_mem_t *mctx, uns
  cleanup_exclusivegranted:
 	(void)isc_condition_destroy(&manager->exclusive_granted);
  cleanup_workavailable:
-	(void)isc_condition_destroy(&manager->work_available);
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_slow]);
+	(void)isc_condition_destroy(&manager->work_available[isc_taskqueue_normal]);
  cleanup_threads:
 	isc_mem_free(mctx, manager->threads);
  cleanup_lock:
@@ -1582,7 +1642,7 @@ isc__taskmgr_destroy(isc_taskmgr_t **man
 	     task = NEXT(task, link)) {
 		LOCK(&task->lock);
 		if (task_shutdown(task))
-			push_readyq(manager, task);
+			push_readyq(manager, task, task->qid);
 		UNLOCK(&task->lock);
 	}
 #ifdef USE_WORKER_THREADS
@@ -1591,7 +1651,8 @@ isc__taskmgr_destroy(isc_taskmgr_t **man
 	 * there's work left to do, and if there are already no tasks left
 	 * it will cause the workers to see manager->exiting.
 	 */
-	BROADCAST(&manager->work_available);
+	BROADCAST(&manager->work_available[isc_taskqueue_normal]);
+	BROADCAST(&manager->work_available[isc_taskqueue_slow]);
 	UNLOCK(&manager->lock);
 
 	/*
@@ -1652,7 +1713,8 @@ isc__taskmgr_ready(isc_taskmgr_t *manage
 		return (false);
 
 	LOCK(&manager->lock);
-	is_ready = !empty_readyq(manager);
+	is_ready = !empty_readyq(manager, isc_taskqueue_normal) ||
+            !empty_readyq(manager, isc_taskqueue_slow);
 	UNLOCK(&manager->lock);
 
 	return (is_ready);
@@ -1669,7 +1731,7 @@ isc__taskmgr_dispatch(isc_taskmgr_t *man
 	if (manager == NULL)
 		return (ISC_R_NOTFOUND);
 
-	dispatch(manager);
+	dispatch(manager, isc_taskqueue_slow);
 
 	return (ISC_R_SUCCESS);
 }
@@ -1693,7 +1755,8 @@ isc__taskmgr_resume(isc_taskmgr_t *manag
 	LOCK(&manager->lock);
 	if (manager->pause_requested) {
 		manager->pause_requested = false;
-		BROADCAST(&manager->work_available);
+		BROADCAST(&manager->work_available[isc_taskqueue_normal]);
+		BROADCAST(&manager->work_available[isc_taskqueue_slow]);
 	}
 	UNLOCK(&manager->lock);
 }
@@ -1778,7 +1841,8 @@ isc__task_endexclusive(isc_task_t *task0
 	LOCK(&manager->lock);
 	REQUIRE(manager->exclusive_requested);
 	manager->exclusive_requested = false;
-	BROADCAST(&manager->work_available);
+	BROADCAST(&manager->work_available[isc_taskqueue_normal]);
+	BROADCAST(&manager->work_available[isc_taskqueue_slow]);
 	UNLOCK(&manager->lock);
 #else
 	UNUSED(task0);
@@ -1804,10 +1868,10 @@ isc__task_setprivilege(isc_task_t *task0
 
 	LOCK(&manager->lock);
 	if (priv && ISC_LINK_LINKED(task, ready_link))
-		ENQUEUE(manager->ready_priority_tasks, task,
+		ENQUEUE(manager->ready_priority_tasks[task->qid], task,
 			ready_priority_link);
 	else if (!priv && ISC_LINK_LINKED(task, ready_priority_link))
-		DEQUEUE(manager->ready_priority_tasks, task,
+		DEQUEUE(manager->ready_priority_tasks[task->qid], task,
 			ready_priority_link);
 	UNLOCK(&manager->lock);
 }
Index: bind-9.11.22/lib/dns/dst_api.c
===================================================================
--- bind-9.11.22.orig/lib/dns/dst_api.c
+++ bind-9.11.22/lib/dns/dst_api.c
@@ -105,6 +105,7 @@ static isc_result_t	frombuffer(dns_name_
 				   dns_rdataclass_t rdclass,
 				   isc_buffer_t *source,
 				   isc_mem_t *mctx,
+				   bool no_rdata,
 				   dst_key_t **keyp);
 
 static isc_result_t	algorithm_status(unsigned int alg);
@@ -741,6 +742,13 @@ isc_result_t
 dst_key_fromdns(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(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;
@@ -769,7 +777,7 @@ dst_key_fromdns(dns_name_t *name, dns_rd
 	}
 
 	result = frombuffer(name, alg, flags, proto, rdclass, source,
-			    mctx, &key);
+			    mctx, no_rdata, &key);
 	if (result != ISC_R_SUCCESS)
 		return (result);
 	key->key_id = id;
@@ -791,7 +799,7 @@ dst_key_frombuffer(dns_name_t *name, uns
 	REQUIRE(dst_initialized);
 
 	result = frombuffer(name, alg, flags, protocol, rdclass, source,
-			    mctx, &key);
+			    mctx, false, &key);
 	if (result != ISC_R_SUCCESS)
 		return (result);
 
@@ -1892,7 +1900,8 @@ computeid(dst_key_t *key) {
 static isc_result_t
 frombuffer(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;
@@ -1917,10 +1926,12 @@ frombuffer(dns_name_t *name, unsigned in
 			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);
+			}
 		}
 	}
 
Index: bind-9.11.22/lib/dns/include/dst/dst.h
===================================================================
--- bind-9.11.22.orig/lib/dns/include/dst/dst.h
+++ bind-9.11.22/lib/dns/include/dst/dst.h
@@ -417,6 +417,10 @@ dst_key_tofile(const dst_key_t *key, int
  */
 
 isc_result_t
+dst_key_fromdns_ex(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(dns_name_t *name, dns_rdataclass_t rdclass,
 		isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp);
 /*%<
Index: bind-9.11.22/lib/dns/win32/libdns.def.in
===================================================================
--- bind-9.11.22.orig/lib/dns/win32/libdns.def.in
+++ bind-9.11.22/lib/dns/win32/libdns.def.in
@@ -1435,6 +1435,7 @@ dst_key_format
 dst_key_free
 dst_key_frombuffer
 dst_key_fromdns
+dst_key_fromdns_ex
 dst_key_fromfile
 dst_key_fromgssapi
 dst_key_fromlabel
Index: bind-9.11.22/CHANGES
===================================================================
--- bind-9.11.22.orig/CHANGES
+++ bind-9.11.22/CHANGES
@@ -1,3 +1,11 @@
+6322.	[security]	Specific DNS answers could cause a denial-of-service
+			condition due to DNS validation taking a long time.
+			(CVE-2023-50387) [GL #4424]
+
+			The same code change also addresses another problem:
+			preparing NSEC3 closest encloser proofs could exhaust
+			available CPU resources. (CVE-2023-50868) [GL #4459]
+
 6315.	[security]	Speed up parsing of DNS messages with many different
 			names. (CVE-2023-4408) [GL #4234]
 
openSUSE Build Service is sponsored by