]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
prevent a possible validator deadlock
authorEvan Hunt <each@isc.org>
Mon, 29 Nov 2021 21:38:56 +0000 (13:38 -0800)
committerEvan Hunt <each@isc.org>
Wed, 8 Dec 2021 18:56:25 +0000 (10:56 -0800)
When processing a CNAME response, a validator could initiate a
query that matches the one waiting on the validator.

lib/dns/include/dns/validator.h
lib/dns/resolver.c
lib/dns/validator.c

index 43da2b4c269fa0989e991950e985a4e706e6066a..da7fd64ef64c3f509419329d1ae02950d76d2003 100644 (file)
@@ -77,6 +77,13 @@ typedef struct dns_validatorevent {
         */
        dns_name_t         *name;
        dns_rdatatype_t type;
+
+       /*
+        * Original query type (may differ from type in the case
+        * of CNAME responses).
+        */
+       dns_rdatatype_t origtype;
+
        /*
         * Rdata and RRSIG (if any) for positive responses.
         */
@@ -161,17 +168,17 @@ ISC_LANG_BEGINDECLS
 
 isc_result_t
 dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
-                    dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
-                    dns_message_t *message, unsigned int options,
-                    isc_task_t *task, isc_taskaction_t action, void *arg,
+                    dns_rdatatype_t origtype, dns_rdataset_t *rdataset,
+                    dns_rdataset_t *sigrdataset, dns_message_t *message,
+                    unsigned int options, isc_task_t *task,
+                    isc_taskaction_t action, void *arg,
                     dns_validator_t **validatorp);
 /*%<
  * Start a DNSSEC validation.
  *
- * This validates a response to the question given by
- * 'name' and 'type'.
+ * This validates a response RRset matching owner 'name' and type 'type'.
  *
- * To validate a positive response, the response data is
+ * When validating a positive response, the response data is
  * given by 'rdataset' and 'sigrdataset'.  If 'sigrdataset'
  * is NULL, the data is presumed insecure and an attempt
  * is made to prove its insecurity by finding the appropriate
@@ -184,11 +191,17 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
  * is implemented yet).  If the complete response message
  * is not available, 'message' is NULL.
  *
- * To validate a negative response, the complete negative response
- * message is given in 'message'.  The 'rdataset', and
+ * When validating a negative response, the complete negative
+ * response message is given in 'message'.  The 'rdataset', and
  * 'sigrdataset' arguments must be NULL, but the 'name' and 'type'
  * arguments must be provided.
  *
+ * 'origtype' is the original query type that generated this
+ * response. This may differ from 'type', if 'type' is CNAME.
+ * This is used for loop detection, to prevent a case in
+ * which a validator attempts to restart the same fetch that was
+ * already waiting for the validator.
+ *
  * The validation is performed in the context of 'view'.
  *
  * When the validation finishes, a dns_validatorevent_t with
index 5c78c410e23c1fd30bef6ef1bd29f07717f7f435..1b1529050faf76fbe35faa865a365957240ee654 100644 (file)
@@ -919,9 +919,10 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
          dns_name_t *name, dns_rdatatype_t type, dns_rdataset_t *rdataset,
          dns_rdataset_t *sigrdataset, unsigned int valoptions,
          isc_task_t *task) {
-       dns_validator_t *validator = NULL;
-       dns_valarg_t *valarg;
        isc_result_t result;
+       dns_validator_t *validator = NULL;
+       dns_valarg_t *valarg = NULL;
+       dns_rdatatype_t origtype = dns_rdatatype_none;
 
        valarg = isc_mem_get(fctx->mctx, sizeof(*valarg));
 
@@ -938,9 +939,12 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
                valoptions &= ~DNS_VALIDATOR_DEFER;
        }
 
-       result = dns_validator_create(fctx->res->view, name, type, rdataset,
-                                     sigrdataset, message, valoptions, task,
-                                     validated, valarg, &validator);
+       if (type == dns_rdatatype_cname) {
+               origtype = fctx->type;
+       }
+       result = dns_validator_create(
+               fctx->res->view, name, type, origtype, rdataset, sigrdataset,
+               message, valoptions, task, validated, valarg, &validator);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
        if (result == ISC_R_SUCCESS) {
                inc_stats(fctx->res, dns_resstatscounter_val);
index fa41322cb7cf01a2d5d7e426149cf5ee4aa66ccd..97bdc271432f35b142b036494c9c4ae83f79a74f 100644 (file)
@@ -1001,19 +1001,24 @@ notfound:
 static inline bool
 check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
               dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) {
-       dns_validator_t *parent;
+       dns_validator_t *parent = NULL;
 
        for (parent = val; parent != NULL; parent = parent->parent) {
-               if (parent->event != NULL && parent->event->type == type &&
+               if (parent->event == NULL) {
+                       continue;
+               }
+
+               if ((parent->event->type == type ||
+                    (parent->event->type == dns_rdatatype_cname &&
+                     parent->event->origtype == type)) &&
                    dns_name_equal(parent->event->name, name) &&
                    /*
                     * As NSEC3 records are meta data you sometimes
                     * need to prove a NSEC3 record which says that
                     * itself doesn't exist.
                     */
-                   (parent->event->type != dns_rdatatype_nsec3 ||
-                    rdataset == NULL || sigrdataset == NULL ||
-                    parent->event->message == NULL ||
+                   (type != dns_rdatatype_nsec3 || rdataset == NULL ||
+                    sigrdataset == NULL || parent->event->message == NULL ||
                     parent->event->rdataset != NULL ||
                     parent->event->sigrdataset != NULL))
                {
@@ -1083,9 +1088,9 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
                  (DNS_VALIDATOR_NOCDFLAG | DNS_VALIDATOR_NONTA));
 
        validator_logcreate(val, name, type, caller, "validator");
-       result = dns_validator_create(val->view, name, type, rdataset, sig,
-                                     NULL, vopts, val->task, action, val,
-                                     &val->subvalidator);
+       result = dns_validator_create(val->view, name, type, dns_rdatatype_none,
+                                     rdataset, sig, NULL, vopts, val->task,
+                                     action, val, &val->subvalidator);
        if (result == ISC_R_SUCCESS) {
                val->subvalidator->parent = val;
                val->subvalidator->depth = val->depth + 1;
@@ -3100,9 +3105,10 @@ validator_start(isc_task_t *task, isc_event_t *event) {
 
 isc_result_t
 dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
-                    dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
-                    dns_message_t *message, unsigned int options,
-                    isc_task_t *task, isc_taskaction_t action, void *arg,
+                    dns_rdatatype_t origtype, dns_rdataset_t *rdataset,
+                    dns_rdataset_t *sigrdataset, dns_message_t *message,
+                    unsigned int options, isc_task_t *task,
+                    isc_taskaction_t action, void *arg,
                     dns_validator_t **validatorp) {
        isc_result_t result = ISC_R_FAILURE;
        dns_validator_t *val;
@@ -3122,6 +3128,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
        event->result = ISC_R_FAILURE;
        event->name = name;
        event->type = type;
+       event->origtype = origtype;
        event->rdataset = rdataset;
        event->sigrdataset = sigrdataset;
        event->message = message;