lib/dns/resolver.c: Convert (dns_view_t *)->weakrefs to isc_refcount_t

There's a deadlock in BIND 9 code where (dns_view_t){ .lock } and
(dns_resolver_t){ .buckets[i].lock } gets locked in different order.  When
view->weakrefs gets converted to a reference counting we can reduce the locking
in dns_view_weakdetach only to cases where it's the last instance of the
dns_view_t object.
This commit is contained in:
Ondřej Surý
2019-08-01 11:42:58 +02:00
parent 18f56f1e3d
commit a7c9a52c89
2 changed files with 30 additions and 22 deletions

View File

@@ -193,9 +193,9 @@ struct dns_view {
/* Locked by themselves. */ /* Locked by themselves. */
isc_refcount_t references; isc_refcount_t references;
isc_refcount_t weakrefs;
/* Locked by lock. */ /* Locked by lock. */
unsigned int weakrefs;
unsigned int attributes; unsigned int attributes;
/* Under owner's locking control. */ /* Under owner's locking control. */
ISC_LINK(struct dns_view) link; ISC_LINK(struct dns_view) link;

View File

@@ -157,9 +157,13 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
view->frozen = false; view->frozen = false;
view->task = NULL; view->task = NULL;
result = isc_refcount_init(&view->references, 1); result = isc_refcount_init(&view->references, 1);
if (result != ISC_R_SUCCESS) if (result != ISC_R_SUCCESS) {
goto cleanup_fwdtable; goto cleanup_fwdtable;
view->weakrefs = 0; }
result = isc_refcount_init(&view->weakrefs, 0);
if (result != ISC_R_SUCCESS) {
goto cleanup_references;
}
view->attributes = (DNS_VIEWATTR_RESSHUTDOWN|DNS_VIEWATTR_ADBSHUTDOWN| view->attributes = (DNS_VIEWATTR_RESSHUTDOWN|DNS_VIEWATTR_ADBSHUTDOWN|
DNS_VIEWATTR_REQSHUTDOWN); DNS_VIEWATTR_REQSHUTDOWN);
view->statickeys = NULL; view->statickeys = NULL;
@@ -169,7 +173,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
view->matchrecursiveonly = false; view->matchrecursiveonly = false;
result = dns_tsigkeyring_create(view->mctx, &view->dynamickeys); result = dns_tsigkeyring_create(view->mctx, &view->dynamickeys);
if (result != ISC_R_SUCCESS) if (result != ISC_R_SUCCESS)
goto cleanup_references; goto cleanup_weakrefs;
view->peers = NULL; view->peers = NULL;
view->order = NULL; view->order = NULL;
view->delonly = NULL; view->delonly = NULL;
@@ -318,6 +322,9 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
dns_tsigkeyring_detach(&view->dynamickeys); dns_tsigkeyring_detach(&view->dynamickeys);
} }
cleanup_weakrefs:
isc_refcount_destroy(&view->weakrefs);
cleanup_references: cleanup_references:
isc_refcount_decrement(&view->references, NULL); isc_refcount_decrement(&view->references, NULL);
isc_refcount_destroy(&view->references); isc_refcount_destroy(&view->references);
@@ -354,12 +361,13 @@ destroy(dns_view_t *view) {
dns_dlzdb_t *dlzdb; dns_dlzdb_t *dlzdb;
REQUIRE(!ISC_LINK_LINKED(view, link)); REQUIRE(!ISC_LINK_LINKED(view, link));
REQUIRE(isc_refcount_current(&view->references) == 0);
REQUIRE(view->weakrefs == 0);
REQUIRE(RESSHUTDOWN(view)); REQUIRE(RESSHUTDOWN(view));
REQUIRE(ADBSHUTDOWN(view)); REQUIRE(ADBSHUTDOWN(view));
REQUIRE(REQSHUTDOWN(view)); REQUIRE(REQSHUTDOWN(view));
isc_refcount_destroy(&view->references);
isc_refcount_destroy(&view->weakrefs);
if (view->order != NULL) if (view->order != NULL)
dns_order_detach(&view->order); dns_order_detach(&view->order);
if (view->peers != NULL) if (view->peers != NULL)
@@ -554,6 +562,7 @@ destroy(dns_view_t *view) {
DESTROYLOCK(&view->new_zone_lock); DESTROYLOCK(&view->new_zone_lock);
DESTROYLOCK(&view->lock); DESTROYLOCK(&view->lock);
isc_refcount_destroy(&view->references); isc_refcount_destroy(&view->references);
isc_refcount_destroy(&view->weakrefs);
isc_mem_free(view->mctx, view->nta_file); isc_mem_free(view->mctx, view->nta_file);
isc_mem_free(view->mctx, view->name); isc_mem_free(view->mctx, view->name);
isc_mem_putanddetach(&view->mctx, view, sizeof(*view)); isc_mem_putanddetach(&view->mctx, view, sizeof(*view));
@@ -567,9 +576,11 @@ static bool
all_done(dns_view_t *view) { all_done(dns_view_t *view) {
if (isc_refcount_current(&view->references) == 0 && if (isc_refcount_current(&view->references) == 0 &&
view->weakrefs == 0 && isc_refcount_current(&view->weakrefs) == 0 &&
RESSHUTDOWN(view) && ADBSHUTDOWN(view) && REQSHUTDOWN(view)) RESSHUTDOWN(view) && ADBSHUTDOWN(view) && REQSHUTDOWN(view))
{
return (true); return (true);
}
return (false); return (false);
} }
@@ -679,9 +690,7 @@ dns_view_weakattach(dns_view_t *source, dns_view_t **targetp) {
REQUIRE(DNS_VIEW_VALID(source)); REQUIRE(DNS_VIEW_VALID(source));
REQUIRE(targetp != NULL && *targetp == NULL); REQUIRE(targetp != NULL && *targetp == NULL);
LOCK(&source->lock); isc_refcount_increment0(&source->weakrefs, NULL);
source->weakrefs++;
UNLOCK(&source->lock);
*targetp = source; *targetp = source;
} }
@@ -689,24 +698,23 @@ dns_view_weakattach(dns_view_t *source, dns_view_t **targetp) {
void void
dns_view_weakdetach(dns_view_t **viewp) { dns_view_weakdetach(dns_view_t **viewp) {
dns_view_t *view; dns_view_t *view;
bool done = false; unsigned int weakrefs;
REQUIRE(viewp != NULL); REQUIRE(viewp != NULL);
view = *viewp; view = *viewp;
REQUIRE(DNS_VIEW_VALID(view)); REQUIRE(DNS_VIEW_VALID(view));
LOCK(&view->lock);
INSIST(view->weakrefs > 0);
view->weakrefs--;
done = all_done(view);
UNLOCK(&view->lock);
*viewp = NULL; *viewp = NULL;
if (done) isc_refcount_decrement(&view->weakrefs, &weakrefs);
destroy(view); if (weakrefs == 0) {
bool done = false;
LOCK(&view->lock);
done = all_done(view);
UNLOCK(&view->lock);
if (done) {
destroy(view);
}
}
} }
static void static void