fix: usr: Fix intermittent named crashes during asynchronous zone operations

Asynchronous zone loading and dumping operations occasionally dispatched tasks
to the wrong internal event loop. This threading violation triggered internal
safety assertions that abruptly terminated named. Strict loop affinity is now
enforced for these tasks, ensuring they execute on their designated threads
and preventing the crashes.

Closes #4882

Merge branch '4882-run-rndc-zone-commands-on-correct-loop' into 'main'

See merge request isc-projects/bind9!11655
This commit is contained in:
Ondřej Surý 2026-03-14 07:45:57 +01:00
commit da6a85dc63
4 changed files with 44 additions and 57 deletions

View file

@ -45,6 +45,8 @@
#include <dns/time.h>
#include <dns/ttl.h>
#include "dns/types.h"
/*!
* Grow the number of dns_rdatalist_t (#RDLSZ) and dns_rdata_t (#RDSZ)
* structures by these sizes when we need to.
@ -387,29 +389,8 @@ gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *token, bool eol,
return ISC_R_SUCCESS;
}
void
dns_loadctx_attach(dns_loadctx_t *source, dns_loadctx_t **target) {
REQUIRE(target != NULL && *target == NULL);
REQUIRE(DNS_LCTX_VALID(source));
isc_refcount_increment(&source->references);
*target = source;
}
void
dns_loadctx_detach(dns_loadctx_t **lctxp) {
dns_loadctx_t *lctx;
REQUIRE(lctxp != NULL);
lctx = *lctxp;
*lctxp = NULL;
REQUIRE(DNS_LCTX_VALID(lctx));
if (isc_refcount_decrement(&lctx->references) == 1) {
loadctx_destroy(lctx);
}
}
ISC_REFCOUNT_DECL(dns_loadctx);
ISC_REFCOUNT_IMPL(dns_loadctx, loadctx_destroy);
static void
incctx_destroy(isc_mem_t *mctx, dns_incctx_t *ictx) {
@ -2621,6 +2602,21 @@ load_done(void *arg) {
dns_loadctx_detach(&lctx);
}
static void
load_enqueue(void *lctx) {
isc_work_enqueue(isc_loop(), load, load_done, lctx);
}
static void
dns_loadctx_enqueue(isc_loop_t *loop, dns_loadctx_t *lctx) {
dns_loadctx_ref(lctx);
if (loop == isc_loop()) {
load_enqueue(lctx);
} else {
isc_async_run(loop, load_enqueue, lctx);
}
}
isc_result_t
dns_master_loadfileasync(const char *master_file, dns_name_t *top,
dns_name_t *origin, dns_rdataclass_t zclass,
@ -2649,8 +2645,8 @@ dns_master_loadfileasync(const char *master_file, dns_name_t *top,
return result;
}
dns_loadctx_attach(lctx, lctxp);
isc_work_enqueue(loop, load, load_done, lctx);
dns_loadctx_enqueue(loop, lctx);
*lctxp = lctx;
return ISC_R_SUCCESS;
}

View file

@ -1354,29 +1354,8 @@ dumpctx_destroy(dns_dumpctx_t *dctx) {
isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
}
void
dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
REQUIRE(DNS_DCTX_VALID(source));
REQUIRE(target != NULL && *target == NULL);
isc_refcount_increment(&source->references);
*target = source;
}
void
dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
dns_dumpctx_t *dctx;
REQUIRE(dctxp != NULL);
dctx = *dctxp;
*dctxp = NULL;
REQUIRE(DNS_DCTX_VALID(dctx));
if (isc_refcount_decrement(&dctx->references) == 1) {
dumpctx_destroy(dctx);
}
}
ISC_REFCOUNT_DECL(dns_dumpctx);
ISC_REFCOUNT_IMPL(dns_dumpctx, dumpctx_destroy);
isc_result_t
dns_dumpctx_serial(dns_dumpctx_t *dctx, uint32_t *serial) {
@ -1728,6 +1707,21 @@ cleanup:
return result;
}
static void
master_dump_enqueue(void *dctx) {
isc_work_enqueue(isc_loop(), master_dump_cb, master_dump_done_cb, dctx);
}
static void
dns_dumpctx_enqueue(isc_loop_t *loop, dns_dumpctx_t *dctx) {
dns_dumpctx_ref(dctx);
if (loop == isc_loop()) {
master_dump_enqueue(dctx);
} else {
isc_async_run(loop, master_dump_enqueue, dctx);
}
}
isc_result_t
dns_master_dumptostreamasync(isc_mem_t *mctx, dns_db_t *db,
dns_dbversion_t *version,
@ -1745,8 +1739,8 @@ dns_master_dumptostreamasync(isc_mem_t *mctx, dns_db_t *db,
dctx->done = done;
dctx->done_arg = done_arg;
dns_dumpctx_attach(dctx, dctxp);
isc_work_enqueue(loop, master_dump_cb, master_dump_done_cb, dctx);
dns_dumpctx_enqueue(loop, dctx);
*dctxp = dctx;
return ISC_R_SUCCESS;
}
@ -1834,8 +1828,8 @@ dns_master_dumpasync(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
dctx->file = file;
dctx->tmpfile = tempname;
dns_dumpctx_attach(dctx, dctxp);
isc_work_enqueue(loop, master_dump_cb, master_dump_done_cb, dctx);
dns_dumpctx_enqueue(loop, dctx);
*dctxp = dctx;
return ISC_R_SUCCESS;

View file

@ -58,6 +58,7 @@ isc_work_enqueue(isc_loop_t *loop, isc_work_cb work_cb,
int r;
REQUIRE(VALID_LOOP(loop));
REQUIRE(isc_loop() == loop);
REQUIRE(work_cb != NULL);
REQUIRE(after_work_cb != NULL);

View file

@ -54,11 +54,7 @@ after_work_cb(void *arg ISC_ATTR_UNUSED) {
static void
work_enqueue_cb(void *arg ISC_ATTR_UNUSED) {
isc_tid_t tid = isc_loopmgr_nloops() - 1;
isc_loop_t *loop = isc_loop_get(tid);
isc_work_enqueue(loop, work_cb, after_work_cb, NULL);
isc_work_enqueue(isc_loop(), work_cb, after_work_cb, NULL);
}
ISC_RUN_TEST_IMPL(isc_work_enqueue) {