From: Aram Sargsyan Date: Fri, 3 Apr 2026 15:55:55 +0000 (+0000) Subject: Fix a bug in "receive secure serial" processing X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5118f3bad62ade03b1a2c345c7dbfa9629ad9c7f;p=thirdparty%2Fbind9.git Fix a bug in "receive secure serial" processing When a DNS UPDATE messages is received, the zone_send_secureserial() function can schedule a new receive_secure_serial() call with a new 'rss' object before a previous one had a chance to be fully processed. This can cause an assertion failure in receive_secure_serial() with a new 'rss' object (when the old one was rescheduled because it got DNS_R_CONTINUE from dns_update_signaturesinc()). In other words: 1. receive_secure_serial() called with rss, sets zone->rss = rss, reschedules because of DNS_R_CONTINUE 2. receive_secure_serial() called with rss_new, INSIST fails because zone->rss != rss_new), i.e. this was called before the old 'rss' was fully processed Change the code logic by introducing a new 'rss_next' field and making sure the old 'rss' is complete before starting processing the new one. --- diff --git a/lib/dns/zone.c b/lib/dns/zone.c index f23b4cba6d7..3655661d061 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -14393,16 +14393,29 @@ receive_secure_serial(void *arg) { LOCK_ZONE(zone); /* - * The receive_secure_serial() is loop-serialized for the zone. Make - * sure there's no processing currently running. + * The receive_secure_serial() is loop-serialized for the zone, but it + * is possible for new serial to arrive before the old processing is + * completed, because the old call could have been rescheduled if + * dns_update_signaturesinc() below returned DNS_R_CONTINUE. */ - - INSIST(zone->rss == NULL || zone->rss == rss); - if (zone->rss != NULL) { + /* There is an existing processing */ UNLOCK_ZONE(zone); + if (zone->rss != rss) { + /* + * A new 'rss' is sandwiched between a previous call + * with the old 'zone->rss' and a future scheduled + * call with the old 'zone->rss'. Reschedule the new + * 'rss' so the old one can be completed first. + */ + isc_async_run(zone->loop, receive_secure_serial, rss); + return; + } } else { - zone->rss = rss; + /* New arrival */ + INSIST(rss == zone->rss_next); + zone->rss = MOVE_OWNERSHIP(zone->rss_next); + dns_diff_init(zone->mctx, &zone->rss_diff); /* @@ -14518,6 +14531,7 @@ receive_secure_serial(void *arg) { &log, zone, zone->rss_db, zone->rss_oldver, zone->rss_newver, &zone->rss_diff, zone->sigvalidityinterval, &zone->rss_state); if (result == DNS_R_CONTINUE) { + /* Signing quantum reached, reschedule the next chunk. */ if (rjournal != NULL) { dns_journal_destroy(&rjournal); } @@ -14622,15 +14636,25 @@ static isc_result_t zone_send_secureserial(dns_zone_t *zone, uint32_t serial) { struct rss *rss = NULL; - rss = isc_mem_get(zone->secure->mctx, sizeof(*rss)); - *rss = (struct rss){ - .serial = serial, - .link = ISC_LINK_INITIALIZER, - }; - INSIST(LOCKED_ZONE(zone->secure)); - zone_iattach(zone->secure, &rss->zone); - isc_async_run(zone->secure->loop, receive_secure_serial, rss); + + if (zone->secure->rss_next != NULL) { + /* + * New version came earlier than the previous one had a + * chance to be processed, just update the serial. + */ + zone->secure->rss_next->serial = serial; + } else { + rss = isc_mem_get(zone->secure->mctx, sizeof(*rss)); + *rss = (struct rss){ + .serial = serial, + .link = ISC_LINK_INITIALIZER, + }; + zone_iattach(zone->secure, &rss->zone); + + zone->secure->rss_next = rss; + isc_async_run(zone->secure->loop, receive_secure_serial, rss); + } DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_SENDSECURE); return ISC_R_SUCCESS; diff --git a/lib/dns/zone_p.h b/lib/dns/zone_p.h index f7428c3a240..4e409443580 100644 --- a/lib/dns/zone_p.h +++ b/lib/dns/zone_p.h @@ -582,6 +582,7 @@ struct dns_zone { dns_db_t *rss_db; dns_zone_t *rss_raw; struct rss *rss; + struct rss *rss_next; dns_update_state_t *rss_state; isc_stats_t *gluecachestats;