diff --git a/CHANGES b/CHANGES index 6f3e839055..7f4e739771 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +4063. [bug] Asynchronous zone loads were not handled + correctly when the zone load was already in + progress; this could trigger a crash in zt.c. + [RT #37573] + 4062. [bug] Fix an out-of-bounds read in RPZ code. If the read succeeded, it doesn't result in a bug during operation. If the read failed, named diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 61014532b8..9f09c1a8f4 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -534,7 +534,14 @@ Fixed some bugs in RFC 5011 trust anchor management, including a memory leak and a possible loss of state - information.[RT #38458] + information. [RT #38458] + + + + + Asynchronous zone loads were not handled correctly when the + zone load was already in progress; this could trigger a crash + in zt.c. [RT #37573] diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 0a789ab036..2d612238c3 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -370,6 +370,15 @@ dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg); * its first argument and 'zone' as its second. (Normally, 'arg' is * expected to point to the zone table but is left undefined for testing * purposes.) + * + * Require: + *\li 'zone' to be a valid zone. + * + * Returns: + *\li #ISC_R_ALREADYRUNNING + *\li #ISC_R_SUCCESS + *\li #ISC_R_FAILURE + *\li #ISC_R_NOMEMORY */ isc_boolean_t diff --git a/lib/dns/zone.c b/lib/dns/zone.c index c0577f38eb..7c42f1ec83 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -1757,7 +1757,7 @@ zone_touched(dns_zone_t *zone) { } static isc_result_t -zone_load(dns_zone_t *zone, unsigned int flags) { +zone_load(dns_zone_t *zone, unsigned int flags, isc_boolean_t locked) { isc_result_t result; isc_time_t now; isc_time_t loadtime; @@ -1766,13 +1766,16 @@ zone_load(dns_zone_t *zone, unsigned int flags) { REQUIRE(DNS_ZONE_VALID(zone)); - LOCK_ZONE(zone); + if (!locked) + LOCK_ZONE(zone); + INSIST(zone != zone->raw); hasraw = inline_secure(zone); if (hasraw) { - result = zone_load(zone->raw, flags); + result = zone_load(zone->raw, flags, ISC_FALSE); if (result != ISC_R_SUCCESS) { - UNLOCK_ZONE(zone); + if (!locked) + UNLOCK_ZONE(zone); return(result); } LOCK_ZONE(zone->raw); @@ -1981,7 +1984,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) { cleanup: if (hasraw) UNLOCK_ZONE(zone->raw); - UNLOCK_ZONE(zone); + if (!locked) + UNLOCK_ZONE(zone); if (db != NULL) dns_db_detach(&db); return (result); @@ -1989,12 +1993,12 @@ zone_load(dns_zone_t *zone, unsigned int flags) { isc_result_t dns_zone_load(dns_zone_t *zone) { - return (zone_load(zone, 0)); + return (zone_load(zone, 0, ISC_FALSE)); } isc_result_t dns_zone_loadnew(dns_zone_t *zone) { - return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT)); + return (zone_load(zone, DNS_ZONELOADFLAG_NOSTAT, ISC_FALSE)); } static void @@ -2002,6 +2006,7 @@ zone_asyncload(isc_task_t *task, isc_event_t *event) { dns_asyncload_t *asl = event->ev_arg; dns_zone_t *zone = asl->zone; isc_result_t result = ISC_R_SUCCESS; + isc_boolean_t load_pending; UNUSED(task); @@ -2010,13 +2015,21 @@ zone_asyncload(isc_task_t *task, isc_event_t *event) { if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) result = ISC_R_CANCELED; isc_event_free(&event); - if (result == ISC_R_CANCELED || - !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) + + if (result == ISC_R_CANCELED) goto cleanup; - zone_load(zone, 0); - + /* Make sure load is still pending */ LOCK_ZONE(zone); + load_pending = ISC_TF(DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)); + + if (!load_pending) { + UNLOCK_ZONE(zone); + goto cleanup; + } + + zone_load(zone, 0, ISC_TRUE); + DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_LOADPENDING); UNLOCK_ZONE(zone); @@ -2042,7 +2055,7 @@ dns_zone_asyncload(dns_zone_t *zone, dns_zt_zoneloaded_t done, void *arg) { /* If we already have a load pending, stop now */ if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADPENDING)) - done(arg, zone, NULL); + return (ISC_R_ALREADYRUNNING); asl = isc_mem_get(zone->mctx, sizeof (*asl)); if (asl == NULL) @@ -2085,9 +2098,10 @@ dns_zone_loadandthaw(dns_zone_t *zone) { isc_result_t result; if (inline_raw(zone)) - result = zone_load(zone->secure, DNS_ZONELOADFLAG_THAW); + result = zone_load(zone->secure, DNS_ZONELOADFLAG_THAW, + ISC_FALSE); else - result = zone_load(zone, DNS_ZONELOADFLAG_THAW); + result = zone_load(zone, DNS_ZONELOADFLAG_THAW, ISC_FALSE); switch (result) { case DNS_R_CONTINUE: