]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Fix TOCTOU race in DNS UPDATE SSU table handling
authorOndřej Surý <ondrej@sury.org>
Wed, 18 Mar 2026 02:55:51 +0000 (03:55 +0100)
committerOndřej Surý <ondrej@sury.org>
Mon, 23 Mar 2026 10:10:48 +0000 (11:10 +0100)
Pass the SSU table through the update event struct from
send_update() to update_action() instead of reading it from the
zone twice.  If rndc reconfig changed the zone's update policy
between the two reads (e.g., from allow-update to update-policy),
send_update() would skip the maxbytype allocation but
update_action() would see a non-NULL ssutable, triggering
INSIST(ssutable == NULL || maxbytype != NULL) and crashing named.

The ssutable reference is now taken once in send_update() and
transferred to update_action() via the event struct, ensuring
both functions see the same value.

lib/ns/update.c

index 425169d87254c30db334b1cdb89c255c800d8d4e..aba0b80456831548bd5efe5ddd6dcaf38ab3b3a7 100644 (file)
@@ -202,6 +202,7 @@ struct update {
        ns_client_t *client;
        isc_result_t result;
        dns_message_t *answer;
+       dns_ssutable_t *ssutable;
        unsigned int *maxbytype;
        size_t maxbytypelen;
 };
@@ -1802,14 +1803,14 @@ send_update(ns_client_t *client, dns_zone_t *zone) {
        *uev = (update_t){
                .zone = zone,
                .client = client,
-               .maxbytype = maxbytype,
+               .ssutable = TAKE_OWNERSHIP(ssutable),
+               .maxbytype = TAKE_OWNERSHIP(maxbytype),
                .maxbytypelen = maxbytypelen,
                .result = ISC_R_SUCCESS,
        };
 
        isc_nmhandle_attach(client->inner.handle, &client->inner.updatehandle);
        isc_async_run(dns_zone_getloop(zone), update_action, uev);
-       maxbytype = NULL;
 
 cleanup:
        if (db != NULL) {
@@ -2608,6 +2609,7 @@ update_action(void *arg) {
        update_t *uev = (update_t *)arg;
        dns_zone_t *zone = uev->zone;
        ns_client_t *client = uev->client;
+       dns_ssutable_t *ssutable = uev->ssutable;
        unsigned int *maxbytype = uev->maxbytype;
        size_t update = 0, maxbytypelen = uev->maxbytypelen;
        isc_result_t result;
@@ -2622,7 +2624,6 @@ update_action(void *arg) {
        dns_message_t *request = client->message;
        dns_rdataclass_t zoneclass;
        dns_name_t *zonename = NULL;
-       dns_ssutable_t *ssutable = NULL;
        dns_fixedname_t tmpnamefixed;
        dns_name_t *tmpname = NULL;
        dns_zoneopt_t options;
@@ -2639,7 +2640,6 @@ update_action(void *arg) {
        CHECK(dns_zone_getdb(zone, &db));
        zonename = dns_db_origin(db);
        zoneclass = dns_db_class(db);
-       dns_zone_getssutable(zone, &ssutable);
        options = dns_zone_getoptions(zone);
 
        is_inline = (!dns_zone_israw(zone) && dns_zone_issecure(zone));