]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
qpzone find() function could set foundname incorrectly
authorEvan Hunt <each@isc.org>
Thu, 19 Dec 2024 22:19:43 +0000 (14:19 -0800)
committerEvan Hunt <each@isc.org>
Fri, 10 Jan 2025 01:03:51 +0000 (17:03 -0800)
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().

lib/dns/qpzone.c
tests/dns/dbversion_test.c

index fa3ac6ac44f93b27537d0240379e819faef38df9..875c39920f9fc69749e199bdf4f03fde4b011da5 100644 (file)
@@ -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;
+                       }
                }
        }
 
index ac6ebca31dd9472f4c7298ef66764163c25ac80f..690cca80366ca6d572fdb306bd66fce3439e3375 100644 (file)
@@ -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);