summaryrefslogtreecommitdiffstats
path: root/debian/patches/0041-CVE-2023-50387-CVE-2023-50868.patch
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/patches/0041-CVE-2023-50387-CVE-2023-50868.patch724
1 files changed, 724 insertions, 0 deletions
diff --git a/debian/patches/0041-CVE-2023-50387-CVE-2023-50868.patch b/debian/patches/0041-CVE-2023-50387-CVE-2023-50868.patch
new file mode 100644
index 0000000..f45dcf7
--- /dev/null
+++ b/debian/patches/0041-CVE-2023-50387-CVE-2023-50868.patch
@@ -0,0 +1,724 @@
+Index: bind9-git/lib/dns/dst_api.c
+===================================================================
+--- bind9-git.orig/lib/dns/dst_api.c
++++ bind9-git/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);
+@@ -740,6 +741,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;
+@@ -768,7 +776,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;
+@@ -790,7 +798,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);
+
+@@ -1888,7 +1896,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;
+@@ -1913,10 +1922,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: bind9-git/lib/dns/include/dns/validator.h
+===================================================================
+--- bind9-git.orig/lib/dns/include/dns/validator.h
++++ bind9-git/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: bind9-git/lib/dns/include/dst/dst.h
+===================================================================
+--- bind9-git.orig/lib/dns/include/dst/dst.h
++++ bind9-git/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: bind9-git/lib/dns/resolver.c
+===================================================================
+--- bind9-git.orig/lib/dns/resolver.c
++++ bind9-git/lib/dns/resolver.c
+@@ -9075,7 +9075,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: bind9-git/lib/dns/validator.c
+===================================================================
+--- bind9-git.orig/lib/dns/validator.c
++++ bind9-git/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,56 +1215,58 @@ 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)
+- goto failure;
++ goto done;
+ 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) == true)
+- {
+- 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);
+ } 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);
+ }
+
+@@ -1626,37 +1634,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 +1677,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: bind9-git/lib/dns/win32/libdns.def.in
+===================================================================
+--- bind9-git.orig/lib/dns/win32/libdns.def.in
++++ bind9-git/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: bind9-git/lib/isc/include/isc/task.h
+===================================================================
+--- bind9-git.orig/lib/isc/include/isc/task.h
++++ bind9-git/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: bind9-git/lib/isc/task.c
+===================================================================
+--- bind9-git.orig/lib/isc/task.c
++++ bind9-git/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);
+@@ -362,7 +364,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);
+@@ -473,10 +481,12 @@ task_ready(isc__task_t *task) {
+ XTRACE("task_ready");
+
+ LOCK(&manager->lock);
+- push_readyq(manager, task);
++ LOCK(&task->lock);
++ 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);
+ }
+@@ -947,13 +957,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));
+ }
+@@ -967,18 +977,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);
+ }
+
+@@ -992,16 +1002,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;
+@@ -1080,26 +1090,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;
+@@ -1263,7 +1273,9 @@ dispatch(isc__taskmgr_t *manager) {
+ * might even hurt rather than help.
+ */
+ #ifdef USE_WORKER_THREADS
+- push_readyq(manager, task);
++ LOCK(&task->lock);
++ push_readyq(manager, task, qid);
++ UNLOCK(&task->lock);
+ #else
+ ENQUEUE(new_ready_tasks, task, ready_link);
+ if ((task->flags & TASK_F_PRIVILEGED) != 0)
+@@ -1281,20 +1293,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
+
+@@ -1306,13 +1322,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);
++ 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, isc_taskqueue_slow);
+
+ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
+ ISC_MSG_EXITING, "exiting"));
+@@ -1331,7 +1371,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 */
+@@ -1398,12 +1439,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,
+@@ -1432,8 +1481,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;
+@@ -1449,10 +1500,22 @@ 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[16]; /* thread name limit on Linux */
++ char name[21]; /* thread name limit on Linux */
+ snprintf(name, sizeof(name), "isc-worker%04u", i);
+ isc_thread_setname(manager->threads[manager->workers],
+ name);
+@@ -1466,7 +1529,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;
+@@ -1481,7 +1544,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:
+@@ -1566,7 +1630,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
+@@ -1575,7 +1639,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);
+
+ /*
+@@ -1636,7 +1701,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);
+@@ -1653,7 +1719,8 @@ isc__taskmgr_dispatch(isc_taskmgr_t *man
+ if (manager == NULL)
+ return (ISC_R_NOTFOUND);
+
+- dispatch(manager);
++ dispatch(manager, isc_taskqueue_normal);
++ dispatch(manager, isc_taskqueue_slow);
+
+ return (ISC_R_SUCCESS);
+ }
+@@ -1677,7 +1744,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);
+ }
+@@ -1753,7 +1821,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);
+@@ -1779,10 +1848,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);
+ }