diff mbox series

[meta-networking,wrynose,1/11] unbound: patch CVE-2026-80225

Message ID 20260927003921.746786-1-ankur.tyagi85@gmail.com
State New
Headers show
Series [meta-networking,wrynose,1/11] unbound: patch CVE-2026-80225 | expand

Commit Message

Ankur Tyagi Sept. 27, 2026, 12:39 a.m. UTC
From: Ankur Tyagi <ankur.tyagi85@gmail.com>

Details:
https://nvd.nist.gov/vuln/detail/cve-2026-80225

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
 .../unbound/unbound/CVE-2026-80225.patch      | 286 ++++++++++++++++++
 .../recipes-support/unbound/unbound_1.24.2.bb |   1 +
 2 files changed, 287 insertions(+)
 create mode 100644 meta-networking/recipes-support/unbound/unbound/CVE-2026-80225.patch
diff mbox series

Patch

diff --git a/meta-networking/recipes-support/unbound/unbound/CVE-2026-80225.patch b/meta-networking/recipes-support/unbound/unbound/CVE-2026-80225.patch
new file mode 100644
index 0000000000..fb7dda7a15
--- /dev/null
+++ b/meta-networking/recipes-support/unbound/unbound/CVE-2026-80225.patch
@@ -0,0 +1,286 @@ 
+From 5d92dbc699c7a347be74b3752b1c3ee690e4dec1 Mon Sep 17 00:00:00 2001
+From: "W.C.A. Wijngaards" <wouter@nlnetlabs.nl>
+Date: Wed, 16 Sep 2026 09:16:29 +0200
+Subject: [PATCH] - Fix CVE-2026-80225, Possible degradation of service from
+ continuous   queries on the same TCP/DoT connection. Thanks to Qifan Zhang
+ from   Palo Alto Networks for the report.
+
+(cherry picked from commit e619ead2dbcde8fc47f03b2ac68987b3377da4c7)
+
+CVE: CVE-2026-80225
+Upstream-Status: Backport [https://github.com/NLnetLabs/unbound/commit/e619ead2dbcde8fc47f03b2ac68987b3377da4c7]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ services/listen_dnsport.c | 12 ++++++-
+ services/listen_dnsport.h |  8 ++++-
+ testcode/testbound.c      | 10 ++++++
+ util/fptr_wlist.c         |  2 ++
+ util/netevent.c           | 66 ++++++++++++++++++++++++++++++++++++++-
+ util/netevent.h           |  9 ++++++
+ 6 files changed, 104 insertions(+), 3 deletions(-)
+
+diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
+index d49d4ad4c..9b6fb557c 100644
+--- a/services/listen_dnsport.c
++++ b/services/listen_dnsport.c
+@@ -2134,7 +2134,7 @@ void listen_start_accept(struct listen_dnsport* listen)
+ }
+ 
+ struct tcp_req_info*
+-tcp_req_info_create(struct sldns_buffer* spoolbuf)
++tcp_req_info_create(struct comm_base* base, struct sldns_buffer* spoolbuf)
+ {
+ 	struct tcp_req_info* req = (struct tcp_req_info*)malloc(sizeof(*req));
+ 	if(!req) {
+@@ -2142,6 +2142,12 @@ tcp_req_info_create(struct sldns_buffer* spoolbuf)
+ 		return NULL;
+ 	}
+ 	memset(req, 0, sizeof(*req));
++	req->read_again_timer = comm_timer_create(base, tcp_read_again_cb, req);
++	if(!req->read_again_timer) {
++		log_err("malloc failure");
++		free(req);
++		return NULL;
++	}
+ 	req->spool_buffer = spoolbuf;
+ 	return req;
+ }
+@@ -2151,6 +2157,7 @@ tcp_req_info_delete(struct tcp_req_info* req)
+ {
+ 	if(!req) return;
+ 	tcp_req_info_clear(req);
++	comm_timer_delete(req->read_again_timer);
+ 	/* cp is pointer back to commpoint that owns this struct and
+ 	 * called delete on us */
+ 	/* spool_buffer is shared udp buffer, not deleted here */
+@@ -2189,6 +2196,9 @@ void tcp_req_info_clear(struct tcp_req_info* req)
+ 	req->done_req_list = NULL;
+ 	req->num_done_req = 0;
+ 	req->read_is_closed = 0;
++
++	if(comm_timer_is_set(req->read_again_timer))
++		comm_timer_disable(req->read_again_timer);
+ }
+ 
+ void
+diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h
+index 963595a1c..79f46705f 100644
+--- a/services/listen_dnsport.h
++++ b/services/listen_dnsport.h
+@@ -345,6 +345,10 @@ struct tcp_req_info {
+ 	int num_done_req;
+ 	/** list of pending writable result packets, malloced one at a time */
+ 	struct tcp_req_done_item* done_req_list;
++	/** the read again timer, when the number of pipelined TCP queries
++	 * is large, it waits, zero time, for a new event loop to service
++	 * the remainder of the TCP traffic on the fd. */
++	struct comm_timer* read_again_timer;
+ };
+ 
+ /**
+@@ -375,10 +379,12 @@ struct tcp_req_done_item {
+  * Create tcp request info structure that keeps track of open
+  * requests on the TCP channel that are resolved at the same time,
+  * and the pending results that have to get written back to that client.
++ * @param base: comm base for read again timer.
+  * @param spoolbuf: shared buffer
+  * @return new structure or NULL on alloc failure.
+  */
+-struct tcp_req_info* tcp_req_info_create(struct sldns_buffer* spoolbuf);
++struct tcp_req_info* tcp_req_info_create(struct comm_base* base,
++	struct sldns_buffer* spoolbuf);
+ 
+ /**
+  * Delete tcp request structure.  Called by owning commpoint.
+diff --git a/testcode/testbound.c b/testcode/testbound.c
+index 063037df4..3a3bcaef6 100644
+--- a/testcode/testbound.c
++++ b/testcode/testbound.c
+@@ -786,3 +786,13 @@ size_t doq_table_quic_size_get(struct doq_table* ATTR_UNUSED(table))
+ 	return 0;
+ }
+ #endif
++
++void tcp_read_again_cb(void* ATTR_UNUSED(arg))
++{
++	/* nothing */
++}
++
++void tcp_more_read_again_cb(void* ATTR_UNUSED(arg))
++{
++	/* nothing */
++}
+diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c
+index 3c863d5e1..2cf3d320e 100644
+--- a/util/fptr_wlist.c
++++ b/util/fptr_wlist.c
+@@ -141,6 +141,8 @@ fptr_whitelist_comm_timer(void (*fptr)(void*))
+ #ifdef UB_ON_WINDOWS
+ 	else if(fptr == &wsvc_cron_cb) return 1;
+ #endif
++	else if(fptr == &tcp_read_again_cb) return 1;
++	else if(fptr == &tcp_more_read_again_cb) return 1;
+ 	else if(fptr == &auth_xfer_timer) return 1;
+ 	else if(fptr == &auth_xfer_probe_timer_callback) return 1;
+ 	else if(fptr == &auth_xfer_transfer_timer_callback) return 1;
+diff --git a/util/netevent.c b/util/netevent.c
+index aedcb5e07..0dde9059c 100644
+--- a/util/netevent.c
++++ b/util/netevent.c
+@@ -122,6 +122,10 @@
+ #define NUM_UDP_PER_SELECT 1
+ #endif
+ 
++/** The number of TCP queries over a TCP connection, per read indication
++ * from select. */
++#define NUM_TCP_PER_SELECT 100
++
+ /** timeout in millisec to wait for write to unblock, packets dropped after.*/
+ #define SEND_BLOCKED_WAIT_TIMEOUT 200
+ /** max number of times to wait for write to unblock, packets dropped after.*/
+@@ -4546,6 +4550,10 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
+ static int
+ tcp_req_info_read_again(int fd, struct comm_point* c)
+ {
++	/* One event-loop visit drains at most this many pipelined queries;
++	 * the rest is re-queued, so that other file descriptors get
++	 * serviced in between. */
++	int budget = NUM_TCP_PER_SELECT;
+ 	while(c->tcp_req_info->read_again) {
+ 		int r;
+ 		c->tcp_req_info->read_again = 0;
+@@ -4562,6 +4570,16 @@ tcp_req_info_read_again(int fd, struct comm_point* c)
+ 			}
+ 			return 0;
+ 		}
++		if(--budget <= 0 && c->tcp_req_info->read_again) {
++			/* Defer the rest of the drain to the next loop turn.
++			 * This uses a zero delay timer. For TLS the undrained
++			 * remainder sits in OpenSSL's user-space buffer. */
++			struct timeval tv;
++			memset(&tv, 0, sizeof(tv));
++			verbose(VERB_ALGO, "Defer tcp_req_info read again");
++			comm_timer_set(c->tcp_req_info->read_again_timer, &tv);
++			return 1;
++		}
+ 	}
+ 	return 1;
+ }
+@@ -4575,6 +4593,7 @@ tcp_more_read_again(int fd, struct comm_point* c)
+ 	/* this continues until the read routines get EAGAIN or so,
+ 	 * and thus does not call the callback, and the bool is 0 */
+ 	int* moreread = c->tcp_more_read_again;
++	int budget = NUM_TCP_PER_SELECT;
+ 	while(moreread && *moreread) {
+ 		*moreread = 0;
+ 		if(!comm_point_tcp_handle_read(fd, c, 0)) {
+@@ -4587,6 +4606,30 @@ tcp_more_read_again(int fd, struct comm_point* c)
+ 			}
+ 			return;
+ 		}
++		if(--budget <= 0 && *moreread) {
++			/* Defer the rest of the drain to the next loop turn.
++			 * This uses a zero delay timer. For TLS the undrained
++			 * remainder sits in OpenSSL's user-space buffer. */
++			struct timeval tv;
++			memset(&tv, 0, sizeof(tv));
++			if(!c->tcp_more_read_again_timer) {
++				c->tcp_more_read_again_timer = comm_timer_create(c->ev->base, tcp_more_read_again_cb, c);
++				if(!c->tcp_more_read_again_timer) {
++					log_err("out of memory for tcp more read again timer");
++					reclaim_tcp_handler(c);
++					if(!c->tcp_do_close) {
++						fptr_ok(fptr_whitelist_comm_point(
++							c->callback));
++						(void)(*c->callback)(c, c->cb_arg,
++							NETEVENT_CLOSED, NULL);
++					}
++					return;
++				}
++			}
++			verbose(VERB_ALGO, "Defer more read again");
++			comm_timer_set(c->tcp_more_read_again_timer, &tv);
++			return;
++		}
+ 	}
+ }
+ 
+@@ -4614,6 +4657,23 @@ tcp_more_write_again(int fd, struct comm_point* c)
+ 	}
+ }
+ 
++void
++tcp_read_again_cb(void* arg)
++{
++	struct tcp_req_info* req = (struct tcp_req_info*)arg;
++	verbose(VERB_ALGO, "tcp_read_again_cb");
++	if(!tcp_req_info_read_again(req->cp->fd, req->cp))
++		return;
++}
++
++void
++tcp_more_read_again_cb(void* arg)
++{
++	struct comm_point* c = (struct comm_point*)arg;
++	verbose(VERB_ALGO, "tcp_more_read_again_cb");
++	tcp_more_read_again(c->fd, c);
++}
++
+ void
+ comm_point_tcp_handle_callback(int fd, short event, void* arg)
+ {
+@@ -6025,7 +6085,7 @@ comm_point_create_tcp_handler(struct comm_base *base,
+ 	c->pp2_enabled = parent->pp2_enabled;
+ 	c->pp2_header_state = pp2_header_none;
+ 	if(spoolbuf) {
+-		c->tcp_req_info = tcp_req_info_create(spoolbuf);
++		c->tcp_req_info = tcp_req_info_create(base, spoolbuf);
+ 		if(!c->tcp_req_info) {
+ 			log_err("could not create tcp commpoint");
+ 			sldns_buffer_free(c->buffer);
+@@ -6584,6 +6644,9 @@ comm_point_close(struct comm_point* c)
+ 		*c->tcp_more_read_again = 0;
+ 	if(c->tcp_more_write_again && *c->tcp_more_write_again)
+ 		*c->tcp_more_write_again = 0;
++	if(c->tcp_more_read_again_timer &&
++		comm_timer_is_set(c->tcp_more_read_again_timer))
++		comm_timer_disable(c->tcp_more_read_again_timer);
+ 
+ 	/* close fd after removing from event lists, or epoll.. is messed up */
+ 	if(c->fd != -1 && !c->do_not_close) {
+@@ -6623,6 +6686,7 @@ comm_point_delete(struct comm_point* c)
+ 		free(c->tcp_handlers);
+ 	}
+ 	free(c->timeout);
++	comm_timer_delete(c->tcp_more_read_again_timer);
+ 	if(c->type == comm_tcp || c->type == comm_local || c->type == comm_http) {
+ 		sldns_buffer_free(c->buffer);
+ #ifdef USE_DNSCRYPT
+diff --git a/util/netevent.h b/util/netevent.h
+index c5114bbbe..dae2c375a 100644
+--- a/util/netevent.h
++++ b/util/netevent.h
+@@ -380,6 +380,9 @@ struct comm_point {
+ 	 * Or leave NULL if it is not used at all. */
+ 	int* tcp_more_write_again;
+ 
++	/** resume timer for tcp_more_read_again */
++	struct comm_timer* tcp_more_read_again_timer;
++
+ 	/** if set, read/write completes:
+ 		read/write state of tcp is toggled.
+ 		buffer reset/bytecount reset.
+@@ -1127,6 +1130,12 @@ void doq_send_pkt(struct comm_point* c, struct doq_pkt_addr* paddr,
+ /** doq timer callback function. */
+ void doq_timer_cb(void* arg);
+ 
++/** tcp read again callback function. For tcp req info listen. */
++void tcp_read_again_cb(void* arg);
++
++/** tcp more read again callback function. For outside network. */
++void tcp_more_read_again_cb(void* arg);
++
+ /**
+  * This routine is published for checks and tests, and is only used internally.
+  * handle libevent callback for timer comm.
diff --git a/meta-networking/recipes-support/unbound/unbound_1.24.2.bb b/meta-networking/recipes-support/unbound/unbound_1.24.2.bb
index faf315876c..0b59d7c9f9 100644
--- a/meta-networking/recipes-support/unbound/unbound_1.24.2.bb
+++ b/meta-networking/recipes-support/unbound/unbound_1.24.2.bb
@@ -43,6 +43,7 @@  SRC_URI = "git://github.com/NLnetLabs/unbound.git;protocol=https;branch=master;t
            file://CVE-2026-55991.patch \
            file://CVE-2026-56416.patch \
            file://CVE-2026-56444.patch \
+           file://CVE-2026-80225.patch \
            "
 
 SRCREV = "f6269baa605d31859f28770e01a24e3677e5f82c"