]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
check for validator/fetch dependency loops
authorEvan Hunt <each@isc.org>
Tue, 30 Nov 2021 19:17:01 +0000 (11:17 -0800)
committerEvan Hunt <each@isc.org>
Wed, 8 Dec 2021 18:56:25 +0000 (10:56 -0800)
It was possible for a deadlock to occur when a fetch started
a validator which started another fetch for the same name.
To address this, we now store pointers from the valdiator object
to the originating fetch, and from the fetch object to the originating
validator (if any), so that potential loops can be detected.

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

index 3aa0ab80ea109c305ad172f8c584bee552f9c193..db0dc608bfb124fce3a121f19e21c9ba14d10012 100644 (file)
@@ -94,11 +94,16 @@ typedef enum { dns_quotatype_zone = 0, dns_quotatype_server } dns_quotatype_t;
 #define DNS_FETCHOPT_NOEDNS0    0x00000008 /*%< Do not use EDNS. */
 #define DNS_FETCHOPT_FORWARDONLY 0x00000010 /*%< Only use forwarders. */
 #define DNS_FETCHOPT_NOVALIDATE         0x00000020 /*%< Disable validation. */
-#define DNS_FETCHOPT_OBSOLETE1  0x00000040 /*%< Obsolete */
-#define DNS_FETCHOPT_WANTNSID   0x00000080 /*%< Request NSID */
-#define DNS_FETCHOPT_PREFETCH   0x00000100 /*%< Do prefetch */
-#define DNS_FETCHOPT_NOCDFLAG   0x00000200 /*%< Don't set CD flag. */
-#define DNS_FETCHOPT_NONTA      0x00000400 /*%< Ignore NTA table. */
+#define DNS_FETCHOPT_VALIDATING                                           \
+       0x00000040                       /*%< This fetch was created by a \
+                                         *   validator; its 'arg' is a   \
+                                         *   dns_validator_t object, and \
+                                         *   can be stored and used for  \
+                                         *   validator loop detection. */
+#define DNS_FETCHOPT_WANTNSID 0x00000080 /*%< Request NSID */
+#define DNS_FETCHOPT_PREFETCH 0x00000100 /*%< Do prefetch */
+#define DNS_FETCHOPT_NOCDFLAG 0x00000200 /*%< Don't set CD flag. */
+#define DNS_FETCHOPT_NONTA    0x00000400 /*%< Ignore NTA table. */
 /* RESERVED ECS                                0x00000000 */
 /* RESERVED ECS                                0x00001000 */
 /* RESERVED ECS                                0x00002000 */
@@ -734,4 +739,19 @@ void
 dns_resolver_setfuzzing(void);
 #endif /* ifdef ENABLE_AFL */
 
+dns_validator_t *
+dns_fetch_validator(dns_fetch_t *fetch);
+/*%<
+ * If a fetch was created with DNS_FETCHOPT_VALIDATING, then the
+ * validator that called it will have been passed in as the 'arg'
+ * parameter to dns_resolver_createfetch(). When another fetch is
+ * joined to the original fetch, this function allows the new caller
+ * to retrieve a pointer to the original caller; this is used in the
+ * validator to detect dependency loops (i.e., conditions in which
+ * a validator creates a fetch which cannot succeed because it's
+ * waiting on the same validator).
+ *
+ * Requires:
+ * \li 'fetch' is valid.
+ */
 ISC_LANG_ENDDECLS
index da7fd64ef64c3f509419329d1ae02950d76d2003..fa015c66540108949e84274e93f72e927e2a1e41 100644 (file)
@@ -84,6 +84,11 @@ typedef struct dns_validatorevent {
         */
        dns_rdatatype_t origtype;
 
+       /*
+        * Original fetch that triggered this validation.
+        */
+       dns_fetch_t *fetch;
+
        /*
         * Rdata and RRSIG (if any) for positive responses.
         */
@@ -129,6 +134,7 @@ struct dns_validator {
        unsigned int          options;
        unsigned int          attributes;
        dns_validatorevent_t *event;
+       dns_fetch_t         *caller;
        dns_fetch_t         *fetch;
        dns_validator_t *subvalidator;
        dns_validator_t *parent;
@@ -171,7 +177,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
                     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,
+                    isc_taskaction_t action, void *arg, dns_fetch_t *fetch,
                     dns_validator_t **validatorp);
 /*%<
  * Start a DNSSEC validation.
@@ -204,6 +210,9 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
  *
  * The validation is performed in the context of 'view'.
  *
+ * If set, 'fetch' references the resolver fetch that triggered
+ * this validation.
+ *
  * When the validation finishes, a dns_validatorevent_t with
  * the given 'action' and 'arg' are sent to 'task'.
  * Its 'result' field will be ISC_R_SUCCESS iff the
index 1b1529050faf76fbe35faa865a365957240ee654..3ebe5a5fd295f5c0e352d74fdd9d02d5af785a5c 100644 (file)
@@ -298,6 +298,7 @@ struct fetchctx {
        /*% Not locked. */
        unsigned int magic;
        dns_resolver_t *res;
+       dns_fetch_t *fetch;
        dns_fixedname_t fname;
        dns_name_t *name;
        dns_rdatatype_t type;
@@ -308,6 +309,7 @@ struct fetchctx {
        isc_mem_t *mctx;
        isc_stdtime_t now;
        isc_task_t *task;
+       dns_validator_t *pval;
 
        /* Atomic */
        isc_refcount_t references;
@@ -942,9 +944,10 @@ valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
        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);
+       result = dns_validator_create(fctx->res->view, name, type, origtype,
+                                     rdataset, sigrdataset, message,
+                                     valoptions, task, validated, valarg,
+                                     fctx->fetch, &validator);
        RUNTIME_CHECK(result == ISC_R_SUCCESS);
        if (result == ISC_R_SUCCESS) {
                inc_stats(fctx->res, dns_resstatscounter_val);
@@ -4630,7 +4633,8 @@ fctx_create(dns_resolver_t *res, isc_task_t *task, const dns_name_t *name,
            dns_rdatatype_t type, const dns_name_t *domain,
            dns_rdataset_t *nameservers, const isc_sockaddr_t *client,
            unsigned int options, unsigned int bucketnum, unsigned int depth,
-           isc_counter_t *qc, fetchctx_t **fctxp) {
+           isc_counter_t *qc, dns_fetch_t *fetch, dns_validator_t *pval,
+           fetchctx_t **fctxp) {
        fetchctx_t *fctx = NULL;
        isc_result_t result;
        isc_result_t iresult;
@@ -4647,9 +4651,11 @@ fctx_create(dns_resolver_t *res, isc_task_t *task, const dns_name_t *name,
 
        fctx = isc_mem_get(res->mctx, sizeof(*fctx));
        *fctx = (fetchctx_t){
+               .fetch = fetch,
                .type = type,
                .qmintype = type,
                .options = options,
+               .pval = pval,
                .task = task,
                .bucketnum = bucketnum,
                .dbucketnum = RES_NOBUCKET,
@@ -10683,9 +10689,16 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
        }
 
        if (fctx == NULL) {
+               void *validator = NULL;
+
+               if ((options & DNS_FETCHOPT_VALIDATING) != 0) {
+                       options &= ~DNS_FETCHOPT_VALIDATING;
+                       validator = arg;
+               }
+
                result = fctx_create(res, task, name, type, domain, nameservers,
                                     client, options, bucketnum, depth, qc,
-                                    &fctx);
+                                    fetch, validator, &fctx);
                if (result != ISC_R_SUCCESS) {
                        goto unlock;
                }
@@ -11463,3 +11476,14 @@ dns_resolver_setnonbackofftries(dns_resolver_t *resolver, unsigned int tries) {
 
        resolver->nonbackofftries = tries;
 }
+
+dns_validator_t *
+dns_fetch_validator(dns_fetch_t *fetch) {
+       fetchctx_t *fctx = NULL;
+
+       REQUIRE(DNS_FETCH_VALID(fetch));
+       fctx = fetch->private;
+       REQUIRE(VALID_FCTX(fctx));
+
+       return (fctx->pval);
+}
index 97bdc271432f35b142b036494c9c4ae83f79a74f..3b6e6759cff91c7df48278ae08451ce9009cd917 100644 (file)
@@ -1004,11 +1004,8 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
        dns_validator_t *parent = NULL;
 
        for (parent = val; parent != NULL; parent = parent->parent) {
-               if (parent->event == NULL) {
-                       continue;
-               }
-
-               if ((parent->event->type == type ||
+               if (parent->event != NULL &&
+                   (parent->event->type == type ||
                     (parent->event->type == dns_rdatatype_cname &&
                      parent->event->origtype == type)) &&
                    dns_name_equal(parent->event->name, name) &&
@@ -1027,6 +1024,26 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
                                      "deadlock: aborting validation");
                        return (true);
                }
+
+               if (parent->caller != NULL) {
+                       dns_validator_t *pv =
+                               dns_fetch_validator(parent->caller);
+
+                       /*
+                        * If the fetch that started this validator
+                        * was started by another validator, we need to
+                        * recursively check for a possible deadlock with
+                        * that one (and its parents) too.
+                        */
+                       if (pv != NULL &&
+                           check_deadlock(pv, name, type, NULL, NULL)) {
+                               validator_log(val, ISC_LOG_DEBUG(3),
+                                             "fetch/validator "
+                                             "loop detected: "
+                                             "aborting validation");
+                               return (true);
+                       }
+               }
        }
        return (false);
 }
@@ -1037,7 +1054,8 @@ check_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
 static inline isc_result_t
 create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
             isc_taskaction_t callback, const char *caller) {
-       unsigned int fopts = 0;
+       isc_result_t result;
+       unsigned int fopts = DNS_FETCHOPT_VALIDATING;
 
        disassociate_rdatasets(val);
 
@@ -1056,10 +1074,12 @@ create_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
        }
 
        validator_logcreate(val, name, type, caller, "fetch");
-       return (dns_resolver_createfetch(
+       result = dns_resolver_createfetch(
                val->view->resolver, name, type, NULL, NULL, NULL, NULL, 0,
                fopts, 0, NULL, val->event->ev_sender, callback, val,
-               &val->frdataset, &val->fsigrdataset, &val->fetch));
+               &val->frdataset, &val->fsigrdataset, &val->fetch);
+
+       return (result);
 }
 
 /*%
@@ -1090,7 +1110,7 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type,
        validator_logcreate(val, name, type, caller, "validator");
        result = dns_validator_create(val->view, name, type, dns_rdatatype_none,
                                      rdataset, sig, NULL, vopts, val->task,
-                                     action, val, &val->subvalidator);
+                                     action, val, NULL, &val->subvalidator);
        if (result == ISC_R_SUCCESS) {
                val->subvalidator->parent = val;
                val->subvalidator->depth = val->depth + 1;
@@ -3108,7 +3128,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
                     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,
+                    isc_taskaction_t action, void *arg, dns_fetch_t *fetch,
                     dns_validator_t **validatorp) {
        isc_result_t result = ISC_R_FAILURE;
        dns_validator_t *val;
@@ -3139,6 +3159,7 @@ dns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type,
        val = isc_mem_get(view->mctx, sizeof(*val));
        *val = (dns_validator_t){ .event = event,
                                  .options = options,
+                                 .caller = fetch,
                                  .task = task,
                                  .action = action,
                                  .arg = arg };