2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 14:07:59 +00:00

Address dns_zt_asyncload races by properly using isc_reference_*.

This commit is contained in:
Mark Andrews
2019-12-13 13:58:47 +11:00
committed by Ondřej Surý
parent 423a627946
commit 72f0e01f5d

View File

@@ -285,36 +285,70 @@ load(dns_zone_t *zone, void *paramsv) {
return (result); return (result);
} }
static void
call_loaddone(dns_zt_t *zt) {
dns_zt_allloaded_t loaddone = zt->loaddone;
void *loaddone_arg = zt->loaddone_arg;
/*
* Set zt->loaddone, zt->loaddone_arg and zt->loadparams to NULL
* before calling loaddone.
*/
zt->loaddone = NULL;
zt->loaddone_arg = NULL;
isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
zt->loadparams = NULL;
/*
* Call the callback last.
*/
if (loaddone != NULL) {
loaddone(loaddone_arg);
}
}
isc_result_t isc_result_t
dns_zt_asyncload(dns_zt_t *zt, bool newonly, dns_zt_asyncload(dns_zt_t *zt, bool newonly,
dns_zt_allloaded_t alldone, void *arg) { dns_zt_allloaded_t alldone, void *arg)
{
isc_result_t result; isc_result_t result;
int pending; uint_fast32_t loads_pending;
REQUIRE(VALID_ZT(zt)); REQUIRE(VALID_ZT(zt));
/*
* Obtain a reference to zt->loads_pending so that asyncload can
* safely decrement both zt->references and zt->loads_pending
* without going to zero.
*/
loads_pending = isc_refcount_increment0(&zt->loads_pending);
INSIST(loads_pending == 0);
/*
* Only one dns_zt_asyncload call at a time should be active so
* these pointers should be NULL. They are set back to NULL
* before the zt->loaddone (alldone) is called in call_loaddone.
*/
INSIST(zt->loadparams == NULL);
INSIST(zt->loaddone == NULL);
INSIST(zt->loaddone_arg == NULL);
zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params)); zt->loadparams = isc_mem_get(zt->mctx, sizeof(struct zt_load_params));
zt->loadparams->dl = doneloading; zt->loadparams->dl = doneloading;
zt->loadparams->newonly = newonly; zt->loadparams->newonly = newonly;
zt->loaddone = alldone;
zt->loaddone_arg = arg;
RWLOCK(&zt->rwlock, isc_rwlocktype_write); RWLOCK(&zt->rwlock, isc_rwlocktype_read);
INSIST(isc_refcount_current(&zt->loads_pending) == 0);
result = dns_zt_apply(zt, false, NULL, asyncload, zt); result = dns_zt_apply(zt, false, NULL, asyncload, zt);
RWUNLOCK(&zt->rwlock, isc_rwlocktype_read);
pending = isc_refcount_current(&zt->loads_pending); /*
* Have all the loads completed?
if (pending != 0) { */
zt->loaddone = alldone; if (isc_refcount_decrement(&zt->loads_pending) == 1) {
zt->loaddone_arg = arg; call_loaddone(zt);
}
RWUNLOCK(&zt->rwlock, isc_rwlocktype_write);
if (pending == 0) {
isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
zt->loadparams = NULL;
alldone(arg);
} }
return (result); return (result);
@@ -332,14 +366,20 @@ asyncload(dns_zone_t *zone, void *zt_) {
REQUIRE(zone != NULL); REQUIRE(zone != NULL);
isc_refcount_increment(&zt->references); isc_refcount_increment(&zt->references);
isc_refcount_increment(&zt->loads_pending); isc_refcount_increment(&zt->loads_pending);
result = dns_zone_asyncload(zone, zt->loadparams->newonly, *zt->loadparams->dl, zt); result = dns_zone_asyncload(zone, zt->loadparams->newonly,
*zt->loadparams->dl, zt);
if (result != ISC_R_SUCCESS) { if (result != ISC_R_SUCCESS) {
uint_fast32_t oldref;
isc_refcount_decrement(&zt->references); /*
isc_refcount_decrement(&zt->loads_pending); * Caller is holding a reference to zt->loads_pending
* and zt->references so these can't decrement to zero.
*/
oldref = isc_refcount_decrement(&zt->loads_pending);
INSIST(oldref > 1);
oldref = isc_refcount_decrement(&zt->references);
INSIST(oldref > 1);
} }
return (ISC_R_SUCCESS); return (ISC_R_SUCCESS);
} }
@@ -528,8 +568,6 @@ dns_zt_apply(dns_zt_t *zt, bool stop, isc_result_t *sub,
*/ */
static isc_result_t static isc_result_t
doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) { doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
dns_zt_allloaded_t alldone = NULL;
void *arg = NULL;
UNUSED(zone); UNUSED(zone);
UNUSED(task); UNUSED(task);
@@ -537,15 +575,7 @@ doneloading(dns_zt_t *zt, dns_zone_t *zone, isc_task_t *task) {
REQUIRE(VALID_ZT(zt)); REQUIRE(VALID_ZT(zt));
if (isc_refcount_decrement(&zt->loads_pending) == 1) { if (isc_refcount_decrement(&zt->loads_pending) == 1) {
alldone = zt->loaddone; call_loaddone(zt);
arg = zt->loaddone_arg;
zt->loaddone = NULL;
zt->loaddone_arg = NULL;
isc_mem_put(zt->mctx, zt->loadparams, sizeof(struct zt_load_params));
zt->loadparams = NULL;
if (alldone != NULL) {
alldone(arg);
}
} }
if (isc_refcount_decrement(&zt->references) == 1) { if (isc_refcount_decrement(&zt->references) == 1) {