From: Evan Hunt Date: Thu, 19 Dec 2024 22:19:43 +0000 (-0800) Subject: qpzone find() function could set foundname incorrectly X-Git-Tag: v9.21.4~4^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ad4bab306c797088cc254d06c99bd41cbad9ee3b;p=thirdparty%2Fbind9.git qpzone find() function could set foundname incorrectly when a requested name is found in the QP trie during a lookup, but its records have been marked as nonexistent by a previous deletion, then it's treated as a partial match, but the foundname could be left pointing to the original qname rather than the parent. this could lead to an assertion failure in query_findclosestnsec3(). --- diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c index fa3ac6ac44f..875c39920f9 100644 --- a/lib/dns/qpzone.c +++ b/lib/dns/qpzone.c @@ -3407,10 +3407,8 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version, if (tresult != DNS_R_CONTINUE) { result = tresult; search.chain.len = i - 1; + dns_name_copy(&n->name, foundname); node = n; - if (foundname != NULL) { - dns_name_copy(&node->name, foundname); - } } } @@ -3678,12 +3676,20 @@ found: /* * We have an exact match for the name, but there are no * active rdatasets in the desired version. That means that - * this node doesn't exist in the desired version, and that - * we really have a partial match. + * this node doesn't exist in the desired version. + * If there's a node above this one, reassign the + * foundname to the parent and treat this as a partial + * match. */ if (!wild) { - NODE_UNLOCK(lock, &nlocktype); - goto partial_match; + unsigned int len = search.chain.len - 1; + if (len > 0) { + NODE_UNLOCK(lock, &nlocktype); + dns_qpchain_node(&search.chain, len - 1, NULL, + (void **)&node, NULL); + dns_name_copy(&node->name, foundname); + goto partial_match; + } } } diff --git a/tests/dns/dbversion_test.c b/tests/dns/dbversion_test.c index ac6ebca31dd..690cca80366 100644 --- a/tests/dns/dbversion_test.c +++ b/tests/dns/dbversion_test.c @@ -170,7 +170,14 @@ ISC_RUN_TEST_IMPL(find) { dns_rdataset_init(&rdataset); res = dns_db_find(db1, dns_rootname, v1, dns_rdatatype_soa, 0, 0, NULL, name, &rdataset, NULL); - assert_int_equal(res, DNS_R_NXDOMAIN); + /* + * Note: in the QPzone database, the root node always exists, + * even if it's empty, so we would get DNS_R_NXRRSET from this + * query. In other databases (including the old RBTDB) the root + * node can be nonexistent, and the query would then return + * DNS_R_NXDOMAIN. Allow for both possibilities. + */ + assert_true(res == DNS_R_NXRRSET || res == DNS_R_NXDOMAIN); if (dns_rdataset_isassociated(&rdataset)) { dns_rdataset_disassociate(&rdataset);