]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix a bug in "receive secure serial" processing
authorAram Sargsyan <aram@isc.org>
Fri, 3 Apr 2026 15:55:55 +0000 (15:55 +0000)
committerArаm Sаrgsyаn <aram@isc.org>
Mon, 22 Jun 2026 20:51:53 +0000 (20:51 +0000)
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.

lib/dns/zone.c
lib/dns/zone_p.h

index f23b4cba6d71aab7bad0f7946dc9aab9b0b1f90b..3655661d0610fd03daea2a26d112b7013ad7d505 100644 (file)
@@ -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;
index f7428c3a240da423fa040db58b12cef05b3524bf..4e4094435809b51167d0a609e3b4de7aa5724c06 100644 (file)
@@ -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;