Commit 5aa13045c2 for bind

commit 5aa13045c27c25eebd78ce8cc243be28b1d45983
Author: OndÅ™ej Surý <ondrej@isc.org>
Date:   Thu Sep 24 16:49:50 2026 +0200

    Allow DS lookups when validating CNAMEs

    check_deadlock() rejected any fetch at the owner of a CNAME being
    validated.  This also blocked the DS lookup needed to prove an apex
    CNAME insecure, causing SERVFAIL for unsigned child zones beneath a
    signed parent.  DS records belong to the parent zone, so the child's
    CNAME must not prevent that lookup.

    Remove the blanket restriction on fetches at a CNAME owner.  The
    resolver already rejects attempts to join an ancestor fetch, which
    handles the circular dependency when a DS query itself receives a
    CNAME and validation tries to fetch the same DS again.

diff --git a/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py b/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py
index f395956658..e4bdc3aab9 100644
--- a/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py
+++ b/bin/tests/system/dnssec_cname_response/tests_cname_rejection.py
@@ -175,15 +175,8 @@ def test_cname_for_validator_dnskey_fetch(ns3):

 def test_ds_cname_does_not_deadlock():
     """
-    A DS query answered with an unsigned CNAME must not send the validator
-    into a self-join deadlock (GL#5878). While proving the CNAME insecure
-    the validator would fetch the DS for the same name, re-entering the
-    in-flight DS fetch it is blocked on and stalling for ~12 seconds until a
-    backstop timer fires. The validator now detects that such a fetch cannot
-    advance the alias chain and aborts, so the client gets SERVFAIL promptly.
-
-    'secure.' is a properly signed zone (so validation reaches the DS query),
-    but its authoritative server answers DS queries with an unsigned CNAME.
+    An unsigned CNAME answer to a DS query makes validation fetch the same DS.
+    Reject the fetch loop promptly instead of waiting for a timeout (GL#5878).
     """
     msg = isctest.query.create("insecure.secure.", "DS")

diff --git a/lib/dns/validator.c b/lib/dns/validator.c
index 5f92d40d6c..a17c27d743 100644
--- a/lib/dns/validator.c
+++ b/lib/dns/validator.c
@@ -1270,9 +1270,10 @@ notfound:
 }

 /*%
- * Returns true if proceeding would stall the SHARED fetch: either the
- * fetch cannot advance an alias chain, or an ancestor is already
- * resolving the same (name, type).
+ * Reject validation of the same (name, type) as an ancestor.
+ *
+ * DS at a CNAME owner may be needed to prove insecurity. Fetch loops are
+ * checked by dns_resolver_createfetch().
  */
 static bool
 check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
@@ -1282,27 +1283,6 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
 			continue;
 		}

-		/*
-		 * Validating a chaining CNAME: a fetch at the alias's own
-		 * name cannot advance the chain (no other type can live at
-		 * a CNAME owner, so e.g. the DS/DNSKEY needed for an
-		 * insecurity proof cannot be there) and would only
-		 * self-join the in-flight fetch.  A chaining DNAME is
-		 * different: it aliases only the names below its owner, so
-		 * the owner itself may legitimately hold the DNSKEY or DS
-		 * this validation needs (e.g. a DNAME at a zone apex).
-		 */
-		if (cur->rdataset != NULL &&
-		    cur->rdataset->attributes.chaining &&
-		    cur->rdataset->type == dns_rdatatype_cname)
-		{
-			validator_log(
-				val, ISC_LOG_DEBUG(3),
-				"fetch would not advance the alias chain: "
-				"aborting validation");
-			return true;
-		}
-
 		/*
 		 * Not a loop: NSEC3 is meta data, so proving a name's
 		 * nonexistence can need the NSEC3 RRset that proves it
@@ -1366,6 +1346,11 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
 		0, fopts, 0, val->qc, val->gqc, val->parent_fetch, val->loop,
 		callback, val, &val->edectx, &val->frdataset,
 		&val->fsigrdataset, &val->fetch);
+	if (result == DNS_R_LOOPDETECTED) {
+		validator_log(val, ISC_LOG_DEBUG(3),
+			      "fetch would join a fetch waiting on this "
+			      "validation: aborting validation");
+	}
 	if (result != ISC_R_SUCCESS) {
 		dns_validator_detach(&val);
 	}
@@ -1374,16 +1359,7 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
 }

 /*%
- * Start a fetch for the DS RRset at 'name', which lives in the parent zone.
- *
- * If the delegation database already has a usable delegation for that parent,
- * pass it to the resolver as a hint so the fetch is anchored at the parent
- * zone cut.  DS is an at-parent type, so the resolver derives the same cut on
- * its own for a hintless query; supplying it explicitly additionally gives the
- * resolver's fetch loop detection a zone cut to match on, which it does not
- * have for a fetch started without a hint.  The lookup fails harmlessly if the
- * parent delegation is expired or does not line up with the labels in 'name',
- * leaving a hintless DS fetch.
+ * Fetch DS from the parent, using a cached delegation hint if available.
  */
 static isc_result_t
 create_ds_fetch(dns_validator_t *val, dns_name_t *name, isc_job_cb callback,