]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix cache flush ordering on NTA expiry
authorOndřej Surý <ondrej@isc.org>
Fri, 20 Mar 2026 13:29:57 +0000 (14:29 +0100)
committerOndřej Surý <ondrej@isc.org>
Fri, 20 Mar 2026 13:35:11 +0000 (14:35 +0100)
dns_view_flushnode() was called in the delete_expired() async
callback, which runs after the query that detected the NTA expiry.
This created a race: the query would proceed with stale cached data
from the NTA period before the flush had a chance to run, resulting
in transient SERVFAIL with EDE 22 (No Reachable Authority).

Move dns_view_flushnode() into dns_ntatable_covered() so the cache
is flushed synchronously when the expiry is detected, before the
query continues.

Also simplify the expiry comparison in delete_expired() to a direct
pointer comparison (nta == pval) instead of comparing expiry
timestamps.

lib/dns/nta.c

index b441a8e8b9dc502ea0fa23ac11d475595e981492..a1593120a978adcb32dbe0cb2be4c014cee0e90e 100644 (file)
@@ -373,7 +373,6 @@ delete_expired(void *arg) {
        isc_result_t result;
        dns_qp_t *qp = NULL;
        void *pval = NULL;
-       bool flushnode = false;
 
        REQUIRE(VALID_NTATABLE(ntatable));
 
@@ -387,17 +386,12 @@ delete_expired(void *arg) {
        dns_qpmulti_write(table, &qp);
        result = dns_qp_getname(qp, &nta->name, DNS_DBNAMESPACE_NORMAL, &pval,
                                NULL);
-       if (result == ISC_R_SUCCESS &&
-           CMM_LOAD_SHARED(((dns__nta_t *)pval)->expiry) ==
-                   CMM_LOAD_SHARED(nta->expiry))
-       {
+       if (result == ISC_R_SUCCESS && nta == pval) {
                char nb[DNS_NAME_FORMATSIZE];
                dns_name_format(&nta->name, nb, sizeof(nb));
                isc_log_write(DNS_LOGCATEGORY_DNSSEC, DNS_LOGMODULE_NTA,
                              ISC_LOG_INFO, "deleting expired NTA at %s", nb);
 
-               flushnode = true;
-
                result = dns_qp_deletename(qp, &nta->name,
                                           DNS_DBNAMESPACE_NORMAL, NULL, NULL);
                RUNTIME_CHECK(result == ISC_R_SUCCESS);
@@ -406,9 +400,6 @@ delete_expired(void *arg) {
        }
        dns_qp_compact(qp, DNS_QPGC_MAYBE);
        dns_qpmulti_commit(table, &qp);
-       if (flushnode) {
-               dns_view_flushnode(view, &nta->name, true);
-       }
 unlock:
        rcu_read_unlock();
        dns__nta_detach(&nta);
@@ -423,15 +414,16 @@ dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
        bool answer = false;
        dns_qpread_t qpr;
        void *pval = NULL;
+       bool flushnode = false;
 
        REQUIRE(VALID_NTATABLE(ntatable));
        REQUIRE(dns_name_isabsolute(name));
 
        rcu_read_lock();
+       dns_view_t *view = rcu_dereference(ntatable->view);
        dns_qpmulti_t *table = rcu_dereference(ntatable->table);
-       if (table == NULL) {
-               rcu_read_unlock();
-               return false;
+       if (view == NULL || table == NULL) {
+               goto unlock;
        }
 
        dns_qpmulti_query(table, &qpr);
@@ -462,6 +454,7 @@ dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
                /* NTA is expired */
                dns__nta_ref(nta);
                dns_ntatable_ref(nta->ntatable);
+               flushnode = true;
                isc_async_run(nta->loop, delete_expired, nta);
                goto done;
        }
@@ -469,6 +462,11 @@ dns_ntatable_covered(dns_ntatable_t *ntatable, isc_stdtime_t now,
        answer = true;
 done:
        dns_qpread_destroy(table, &qpr);
+
+       if (nta != NULL && flushnode) {
+               dns_view_flushnode(view, &nta->name, true);
+       }
+unlock:
        rcu_read_unlock();
 
        return answer;