From: Ondřej Surý Date: Thu, 9 Apr 2026 10:45:30 +0000 (+0200) Subject: Account transient delegsets against the caller's memory context X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=876a896f0fc70c04f4263c33b1fde310e7aee0c6;p=thirdparty%2Fbind9.git Account transient delegsets against the caller's memory context dns_delegset_fromnsrdataset() used isc_g_mctx for the transient delegset it builds from a DNS NS rdataset. That hides delegation data in the global default context instead of accounting it against the subsystem that owns it: a resolver fctx, a view, or a query context. Take an explicit mctx parameter so callers can direct the allocation to the right place, and update the three call sites: - lib/dns/view.c:1189 (dns_view_bestzonecut fallback) uses view->mctx - lib/dns/resolver.c:7071 (resume_dslookup) uses fctx->mctx - lib/ns/query.c:8672 (query_delegation_recurse) uses the client manager's mctx Also tighten delegdb cleanup to run inside the same write transaction as the insert: delegdb_node_prepare() now returns the size of the new node, and delegdb_cleanup() takes the caller's open qp so that the overmem reclamation and the insert share one commit instead of doing two nested write transactions. --- diff --git a/lib/dns/deleg.c b/lib/dns/deleg.c index 28fb777ef88..df989c3b742 100644 --- a/lib/dns/deleg.c +++ b/lib/dns/deleg.c @@ -458,19 +458,15 @@ dns_delegset_addns(dns_delegset_t *delegset, dns_deleg_t *deleg, } static void -delegdb_cleanup(dns_delegdb_t *delegdb, dns_qpmulti_t *nodes) { - dns_qp_t *qp = NULL; +delegdb_cleanup(dns_qp_t *qp, dns_delegdb_t *delegdb, size_t requested) { delegdb_node_t *node = NULL; size_t reclaimed = 0; - size_t requested = 0; if (!isc_mem_isovermem(delegdb->mctx)) { return; } requested = delegdb->hiwater - delegdb->lowater; - dns_qpmulti_write(nodes, &qp); - while (reclaimed < requested) { node = ISC_SIEVE_NEXT(delegdb->lru[isc_tid()], visited, link); @@ -483,9 +479,6 @@ delegdb_cleanup(dns_delegdb_t *delegdb, dns_qpmulti_t *nodes) { (void)dns_qp_deletename(qp, &node->zonecut, DNS_DBNAMESPACE_NORMAL, NULL, NULL); } - - dns_qp_compact(qp, DNS_QPGC_ALL); - dns_qpmulti_commit(nodes, &qp); } static size_t @@ -517,13 +510,10 @@ delegdb_node_size(const dns_name_t *zonecut, dns_delegset_t *delegset) { return sz; } -static void -delegdb_node_prepare(dns_delegdb_t *delegdb, dns_qpmulti_t *nodes, - isc_stdtime_t now, dns_ttl_t ttl, +static size_t +delegdb_node_prepare(dns_delegdb_t *delegdb, isc_stdtime_t now, dns_ttl_t ttl, const dns_name_t *zonecut, dns_delegset_t *delegset, delegdb_node_t **nodep) { - delegdb_cleanup(delegdb, nodes); - if (ttl == 0) { ttl = 1; } @@ -542,6 +532,8 @@ delegdb_node_prepare(dns_delegdb_t *delegdb, dns_qpmulti_t *nodes, dns_delegdb_attach(delegdb, &(*nodep)->delegdb); dns_delegset_attach(delegset, &(*nodep)->delegset); dns_name_dup(zonecut, delegdb->mctx, &(*nodep)->zonecut); + + return sizeof(**nodep) + (*nodep)->size; } isc_result_t @@ -593,13 +585,16 @@ dns_delegset_insert(dns_delegdb_t *delegdb, const dns_name_t *zonecut, * clean up expired/least recently used delegation, then allocate and * initialize a new node. */ - delegdb_node_prepare(delegdb, nodes, now, ttl, zonecut, delegset, - &node); + size_t requested = delegdb_node_prepare(delegdb, now, ttl, zonecut, + delegset, &node); /* * Add the node in the DB */ dns_qpmulti_write(nodes, &qp); + + delegdb_cleanup(qp, delegdb, requested); + if (result == ISC_R_SUCCESS) { /* * A node at the same zonecut exists, and it is expired. Ignore @@ -821,7 +816,7 @@ dns_delegdb_dump(dns_delegdb_t *delegdb, bool expired, FILE *fp) { } void -dns_delegset_fromnsrdataset(dns_rdataset_t *rdataset, +dns_delegset_fromnsrdataset(isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_delegset_t **delegsetp) { dns_delegset_t *delegset = NULL; dns_deleg_t *deleg = NULL; @@ -834,17 +829,17 @@ dns_delegset_fromnsrdataset(dns_rdataset_t *rdataset, REQUIRE(rdataset->type == dns_rdatatype_ns); - delegset = isc_mem_get(isc_g_mctx, sizeof(*delegset)); + delegset = isc_mem_get(mctx, sizeof(*delegset)); *delegset = (dns_delegset_t){ .magic = DNS_DELEGSET_MAGIC, + .mctx = isc_mem_ref(mctx), .references = ISC_REFCOUNT_INITIALIZER(1), .delegs = ISC_LIST_INITIALIZER, .expires = rdataset->ttl + isc_stdtime_now(), .staticstub = rdataset->attributes.staticstub }; - isc_mem_attach(isc_g_mctx, &delegset->mctx); - deleg = isc_mem_get(isc_g_mctx, sizeof(*deleg)); + deleg = isc_mem_get(delegset->mctx, sizeof(*deleg)); *deleg = (dns_deleg_t){ .addresses = ISC_LIST_INITIALIZER, .names = ISC_LIST_INITIALIZER, .type = DNS_DELEGTYPE_NS_NAMES, diff --git a/lib/dns/include/dns/deleg.h b/lib/dns/include/dns/deleg.h index 4a7bccb0041..907d4ffc688 100644 --- a/lib/dns/include/dns/deleg.h +++ b/lib/dns/include/dns/deleg.h @@ -206,7 +206,7 @@ dns_delegdb_dump(dns_delegdb_t *db, bool expired, FILE *fp); * (which accepts only delegset allocated using `dns_deleg_alloc*()` APIs. */ void -dns_delegset_fromnsrdataset(dns_rdataset_t *rdataset, +dns_delegset_fromnsrdataset(isc_mem_t *mctx, dns_rdataset_t *rdataset, dns_delegset_t **delegsetp); /* diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index b2e925f55b0..65da6ae1554 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -7068,7 +7068,7 @@ resume_dslookup(void *arg) { case ISC_R_SUCCESS: FCTXTRACE("resuming DS lookup"); - dns_delegset_fromnsrdataset(frdataset, &delegset); + dns_delegset_fromnsrdataset(fctx->mctx, frdataset, &delegset); dns_rdataset_cleanup(frdataset); if (delegset == NULL) { diff --git a/lib/dns/view.c b/lib/dns/view.c index 6208a1bfc43..1637131a6ca 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -1186,7 +1186,7 @@ dns_view_bestzonecut(dns_view_t *view, const dns_name_t *name, * the same, and this avoid adding extra code here to extract * A/AAAA rdataset if any. */ - dns_delegset_fromnsrdataset(&rdataset, delegsetp); + dns_delegset_fromnsrdataset(view->mctx, &rdataset, delegsetp); } dns_rdataset_cleanup(&rdataset); diff --git a/lib/ns/query.c b/lib/ns/query.c index b801b781e5f..b41e8c980d8 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -8669,7 +8669,8 @@ query_delegation_recurse(query_ctx_t *qctx) { qctx->client->inner.now, 0, true, true, &delegset); if (tresult != ISC_R_SUCCESS) { - dns_delegset_fromnsrdataset(qctx->rdataset, &delegset); + dns_delegset_fromnsrdataset(qctx->client->manager->mctx, + qctx->rdataset, &delegset); fname = qctx->fname; } diff --git a/tests/dns/deleg_test.c b/tests/dns/deleg_test.c index e27b7b1eb74..8082b670914 100644 --- a/tests/dns/deleg_test.c +++ b/tests/dns/deleg_test.c @@ -587,7 +587,7 @@ cleanuptests_phase3(void *arg) { dns_delegset_t *delegset = NULL; isc_result_t result; - assert_int_in_range(isc_mem_inuse(db->mctx), 4000000, 4100000); + assert_int_in_range(isc_mem_inuse(db->mctx), 8000000, 8100000); /* * baz. is there, but bar. is gone, as it has been