]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix QP chain on partial match
authorEvan Hunt <each@isc.org>
Mon, 13 May 2024 22:56:15 +0000 (15:56 -0700)
committerEvan Hunt <each@isc.org>
Tue, 14 May 2024 19:58:46 +0000 (12:58 -0700)
When searching for a requested name in dns_qp_lookup(), we may add
a leaf node to the QP chain, then subsequently determine that the
branch we were on was a dead end. When that happens, the chain can be
left holding a pointer to a node that is *not* an ancestor of the
requested name.

We correct for this by unwinding any chain links with an offset
value greater or equal to that of the node we found.

lib/dns/qp.c

index 3f8b0ec2938fac89c516796854d72269cb04cf51..3b73f4755b8a9e80056e1b6ca06792ca5ce9b140 100644 (file)
@@ -2241,6 +2241,23 @@ fix_iterator(dns_qpreader_t *qp, dns_qpiter_t *iter, dns_qpkey_t search,
        }
 }
 
+/*
+ * When searching for a requested name in dns_qp_lookup(), we might add
+ * a leaf node to the chain, then subsequently determine that it was a
+ * dead end. When this happens, the chain can be left holding a node
+ * that is *not* an ancestor of the requested name. We correct for that
+ * here.
+ */
+static void
+fix_chain(dns_qpchain_t *chain, size_t offset) {
+       while (chain->len > 0 && chain->chain[chain->len - 1].offset >= offset)
+       {
+               chain->len--;
+               chain->chain[chain->len].node = NULL;
+               chain->chain[chain->len].offset = 0;
+       }
+}
+
 isc_result_t
 dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
              dns_name_t *foundname, dns_qpiter_t *iter, dns_qpchain_t *chain,
@@ -2364,16 +2381,20 @@ dns_qp_lookup(dns_qpreadable_t qpr, const dns_name_t *name,
        foundlen = leaf_qpkey(qp, n, found);
        offset = qpkey_compare(search, searchlen, found, foundlen);
 
+       /* the search ended with an exact or partial match */
        if (offset == QPKEY_EQUAL || offset == foundlen) {
+               isc_result_t result = ISC_R_SUCCESS;
+
+               if (offset == foundlen) {
+                       fix_chain(chain, offset);
+                       result = DNS_R_PARTIALMATCH;
+               }
+               add_link(chain, n, offset);
+
                SET_IF_NOT_NULL(pval_r, leaf_pval(n));
                SET_IF_NOT_NULL(ival_r, leaf_ival(n));
                maybe_set_name(qp, n, foundname);
-               add_link(chain, n, offset);
-               if (offset == QPKEY_EQUAL) {
-                       return (ISC_R_SUCCESS);
-               } else {
-                       return (DNS_R_PARTIALMATCH);
-               }
+               return (result);
        }
 
        /*