]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix for mesh new client and mesh new callback to rollback the master
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 26 May 2026 14:20:11 +0000 (16:20 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Tue, 26 May 2026 14:20:11 +0000 (16:20 +0200)
  added address, tcp mesh state and callback when there is a failure
  to initialize. This fixes the mesh accounting of reply addresses.
  Thanks to Xin Wang, Jiapeng Li, and Jiajia Liu, Northwestern
  Polytechnical University, for the report

doc/Changelog
services/mesh.c
services/mesh.h

index 30a6d8a043ec27c3e89bd0a0d13cfa124100d7d5..4579cee29bcb55dbeb9051dd1f20fba70b3a359f 100644 (file)
@@ -1,3 +1,10 @@
+26 May 2026: Wouter
+       - Fix for mesh new client and mesh new callback to rollback the
+         added address, tcp mesh state and callback when there is a failure
+         to initialize. This fixes the mesh accounting of reply addresses.
+         Thanks to Xin Wang, Jiapeng Li, and Jiajia Liu, Northwestern
+         Polytechnical University, for the report
+
 20 May 2026: Wouter
        - Fix CVE-2026-33278, Possible remote code execution during DNSSEC
          validation. Thanks to Qifan Zhang, Palo Alto Networks, for the report.
index 04c799751fa8e0fb1f00e95b019a8000150d8908..ef7a5aad713669174d671aafdadc106936a2db1b 100644 (file)
@@ -424,6 +424,44 @@ mesh_serve_expired_init(struct mesh_state* mstate, int timeout)
        return 1;
 }
 
+/** remove a reply without accounting, rollback the add reply. */
+static void
+mesh_remove_reply_without_accounting(struct mesh_state* s,
+       struct mesh_reply* todel)
+{
+       struct mesh_reply* r, *prev = NULL;
+       for(r = s->reply_list; r; r = r->next) {
+               if(r == todel) {
+                       if(prev)
+                               prev->next = r->next;
+                       else    s->reply_list = r->next;
+                       r->next = NULL;
+                       /* todel is allocated in region */
+                       return;
+               }
+               prev = r;
+       }
+}
+
+/** remove a callback without accounting, rollback the add reply. */
+static void
+mesh_remove_callback_without_accounting(struct mesh_state* s,
+       struct mesh_cb* todel)
+{
+       struct mesh_cb* r, *prev = NULL;
+       for(r = s->cb_list; r; r = r->next) {
+               if(r == todel) {
+                       if(prev)
+                               prev->next = r->next;
+                       else    s->cb_list = r->next;
+                       r->next = NULL;
+                       /* todel is allocated in region */
+                       return;
+               }
+               prev = r;
+       }
+}
+
 void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
        struct respip_client_info* cinfo, uint16_t qflags,
        struct edns_data* edns, struct comm_reply* rep, uint16_t qid,
@@ -433,7 +471,8 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
        int unique = unique_mesh_state(edns->opt_list_in, mesh->env);
        int was_detached = 0;
        int was_noreply = 0;
-       int added = 0;
+       int added = 0, added_reply_without_accounting = 0, added_tcp = 0;
+       struct mesh_reply* repadded = NULL;
        int timeout = mesh->env->cfg->serve_expired?
                mesh->env->cfg->serve_expired_client_timeout:0;
        struct sldns_buffer* r_buffer = rep->c->buffer;
@@ -540,16 +579,18 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
                }
        }
        /* add reply to s */
-       if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo)) {
+       if(!mesh_state_add_reply(s, edns, rep, qid, qflags, qinfo, &repadded)) {
                log_err("mesh_new_client: out of memory; SERVFAIL");
                goto servfail_mem;
        }
+       added_reply_without_accounting = 1;
        if(rep->c->tcp_req_info) {
                if(!tcp_req_info_add_meshstate(rep->c->tcp_req_info, mesh, s)) {
                        log_err("mesh_new_client: out of memory add tcpreqinfo");
                        goto servfail_mem;
                }
        }
+       added_tcp = 1;
        if(rep->c->use_h2) {
                http2_stream_add_meshstate(rep->c->h2_stream, mesh, s);
        }
@@ -569,6 +610,7 @@ void mesh_new_client(struct mesh_area* mesh, struct query_info* qinfo,
                }
        }
 #endif
+       added_reply_without_accounting = 0;
        infra_wait_limit_inc(mesh->env->infra_cache, rep, *mesh->env->now,
                mesh->env->cfg);
        /* update statistics */
@@ -606,6 +648,11 @@ servfail_mem:
        if(rep->c->use_h2)
                http2_stream_remove_mesh_state(rep->c->h2_stream);
        comm_point_send_reply(rep);
+       if(added_reply_without_accounting) {
+               mesh_remove_reply_without_accounting(s, repadded);
+               if(added_tcp && rep->c->tcp_req_info)
+                       tcp_req_info_remove_mesh_state(rep->c->tcp_req_info, s);
+       }
        if(added)
                mesh_state_delete(&s->s);
        return;
@@ -623,6 +670,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
        int was_detached = 0;
        int was_noreply = 0;
        int added = 0;
+       struct mesh_cb* add_cb = NULL;
        uint16_t mesh_flags = qflags&(BIT_RD|BIT_CD);
        if(!unique)
                s = mesh_area_find(mesh, NULL, qinfo, mesh_flags, 0, 0);
@@ -668,13 +716,14 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
                }
        }
        /* add reply to s */
-       if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags)) {
+       if(!mesh_state_add_cb(s, edns, buf, cb, cb_arg, qid, qflags, &add_cb)) {
                if(added)
                        mesh_state_delete(&s->s);
                return 0;
        }
        /* add serve expired timer if not already there */
        if(timeout && !mesh_serve_expired_init(s, timeout)) {
+               mesh_remove_callback_without_accounting(s, add_cb);
                if(added)
                        mesh_state_delete(&s->s);
                return 0;
@@ -685,6 +734,7 @@ mesh_new_callback(struct mesh_area* mesh, struct query_info* qinfo,
                (mesh->env->cachedb_enabled &&
                 mesh->env->cfg->cachedb_check_when_serve_expired)) {
                if(!mesh_serve_expired_init(s, -1)) {
+                       mesh_remove_callback_without_accounting(s, add_cb);
                        if(added)
                                mesh_state_delete(&s->s);
                        return 0;
@@ -1939,7 +1989,7 @@ int mesh_state_del_cb(struct mesh_state* s, mesh_cb_func_type cb, void* cb_arg)
 
 int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
         sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
-       uint16_t qid, uint16_t qflags)
+       uint16_t qid, uint16_t qflags, struct mesh_cb** result)
 {
        struct mesh_cb* r = regional_alloc(s->s.region,
                sizeof(struct mesh_cb));
@@ -1963,13 +2013,14 @@ int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
        r->qflags = qflags;
        r->next = s->cb_list;
        s->cb_list = r;
+       *result = r;
        return 1;
 
 }
 
 int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
         struct comm_reply* rep, uint16_t qid, uint16_t qflags,
-        const struct query_info* qinfo)
+        const struct query_info* qinfo, struct mesh_reply** result)
 {
        struct mesh_reply* r = regional_alloc(s->s.region,
                sizeof(struct mesh_reply));
@@ -2048,6 +2099,7 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
                r->local_alias = NULL;
 
        s->reply_list = r;
+       *result = r;
        return 1;
 }
 
index cd10bf462afab9da8a3b8c289c0159c783281ffb..8f8b8a169228cdc9f6ae68cff03ddea9af1698fd 100644 (file)
@@ -562,11 +562,12 @@ int mesh_state_attachment(struct mesh_state* super, struct mesh_state* sub);
  * @param qid: ID of reply.
  * @param qflags: original query flags.
  * @param qinfo: original query info.
+ * @param result: the allocated reply structure, for rollback.
  * @return: 0 on alloc error.
  */
 int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
        struct comm_reply* rep, uint16_t qid, uint16_t qflags,
-       const struct query_info* qinfo);
+       const struct query_info* qinfo, struct mesh_reply** result);
 
 /**
  * Create new callback structure and attach it to a mesh state.
@@ -578,11 +579,12 @@ int mesh_state_add_reply(struct mesh_state* s, struct edns_data* edns,
  * @param cb_arg: callback user arg.
  * @param qid: ID of reply.
  * @param qflags: original query flags.
+ * @param result: the allocated callback structure, for rollback.
  * @return: 0 on alloc error.
  */
 int mesh_state_add_cb(struct mesh_state* s, struct edns_data* edns,
         struct sldns_buffer* buf, mesh_cb_func_type cb, void* cb_arg,
-       uint16_t qid, uint16_t qflags);
+       uint16_t qid, uint16_t qflags, struct mesh_cb** result);
 
 /**
  * Run the mesh. Run all runnable mesh states. Which can create new