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:
committed by
Ondřej Surý
parent
423a627946
commit
72f0e01f5d
100
lib/dns/zt.c
100
lib/dns/zt.c
@@ -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) {
|
||||||
|
Reference in New Issue
Block a user