[Buildroot] [PATCH for 2025.02.x] package/libldns: add patch for CVE-2026-10846

Thomas Perale thomas.perale at mind.be
Fri Sep 4 12:17:07 UTC 2026


In reply of:
> This fixes the following vulnerability:
> - CVE-2026-10846:
>     NLnet Labs ldns 1.2.0 up to and including versions 1.9.0, when used in
>     applications as (stub) resolver over UDP, lacks matching the query
>     destination address and port with the response source address and
>     port. Furthermore not the query ID, neither the question of the query
>     is matched with that of the response. This makes applications, that
>     use ldns for (stub) resolver functionality over UDP, vulnerable for
>     off-path poisoning attacks. The drill tool, which is shipped with
>     ldns, suffers from this vulnerability.
>     https://www.cve.org/CVERecord?id=CVE-2026-10846
>     https://www.nlnetlabs.nl/downloads/ldns/CVE-2026-10846.txt
> 
> Signed-off-by: Titouan Christophe <titouan.christophe at mind.be>

Applied to 2025.02.x. Thanks

> ---
>  package/libldns/0001-fix-cve-2026-10846.patch | 225 ++++++++++++++++++
>  package/libldns/libldns.mk                    |   3 +
>  2 files changed, 228 insertions(+)
>  create mode 100644 package/libldns/0001-fix-cve-2026-10846.patch
> 
> diff --git a/package/libldns/0001-fix-cve-2026-10846.patch b/package/libldns/0001-fix-cve-2026-10846.patch
> new file mode 100644
> index 0000000000..989cd7c958
> --- /dev/null
> +++ b/package/libldns/0001-fix-cve-2026-10846.patch
> @@ -0,0 +1,225 @@
> +From f179ebe97b0c7309edb87a14d6f73119dbecb708 Mon Sep 17 00:00:00 2001
> +From: Titouan Christophe <titouan.christophe at mind.be>
> +Date: Tue, 1 Sep 2026 19:09:47 +0200
> +Subject: [PATCH] Fix CVE-2026-10846
> +
> +== Summary
> +When ldns is used by applications for (stub) resolving, it does not
> +sufficiently verify that received responses belong to a sent query.
> +
> +== Affected products
> +ldns 1.2.0 up to and including 1.9.0
> +
> +== Description
> +NLnet Labs ldns 1.2.0 up to and including versions 1.9.0, when used in
> +applications as (stub) resolver over UDP, lacks matching the query destination
> +address and port with the response source address and port. Furthermore not the
> +query ID, neither the question of the query is matched with that of the
> +response. This makes applications, that use ldns for (stub) resolver
> +functionality, vulnerable for off-path poisoning attacks.
> +
> +The drill tool, which is shipped with ldns and uses ldns for stub resolving,
> +inherently suffers from this vulnerability too.
> +
> +== Solution
> +Download a patched version of ldns, or apply the patch manually
> +
> +CVE: CVE-2026-10846
> +Upstream: https://nlnetlabs.nl/downloads/ldns/patch_cve_2026-10846.diff
> +Signed-off-by: Titouan Christophe <titouan.christophe at mind.be>
> +---
> + error.c      |  8 +++++
> + ldns/error.h |  6 +++-
> + net.c        | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++--
> + 3 files changed, 103 insertions(+), 3 deletions(-)
> +
> +diff --git a/error.c b/error.c
> +index 50cdc630..4fc05d6d 100644
> +--- a/error.c
> ++++ b/error.c
> +@@ -189,6 +189,14 @@ ldns_lookup_table ldns_error_str[] = {
> + 	{ LDNS_STATUS_EDE_OPTION_MALFORMED,
> + 		"The extended error code option is malformed, expected "
> + 		"at least 2 bytes of option data" },
> ++	{ LDNS_STATUS_EQUAL_RR,
> ++		"An identical RR already existed in the zone" },
> ++	{ LDNS_STATUS_ID_DID_NOT_MATCH,
> ++		"Response ID did not match the query ID" },
> ++	{ LDNS_STATUS_QDCOUNT_MUST_BE_ONE,
> ++		"The query section MUST contain exactly one question" },
> ++	{ LDNS_STATUS_QUERY_DID_NOT_MATCH,
> ++		"The question in the response did not match the query" },
> + 	{ 0, NULL }
> + };
> + 
> +diff --git a/ldns/error.h b/ldns/error.h
> +index 011df284..41d64cc0 100644
> +--- a/ldns/error.h
> ++++ b/ldns/error.h
> +@@ -143,7 +143,11 @@ enum ldns_enum_status {
> + 	LDNS_STATUS_SVCPARAM_KEY_MORE_THAN_ONCE,
> + 	LDNS_STATUS_INVALID_SVCPARAM_VALUE,
> + 	LDNS_STATUS_NOT_EDE,
> +-	LDNS_STATUS_EDE_OPTION_MALFORMED
> ++	LDNS_STATUS_EDE_OPTION_MALFORMED,
> ++	LDNS_STATUS_EQUAL_RR,
> ++	LDNS_STATUS_ID_DID_NOT_MATCH,
> ++	LDNS_STATUS_QDCOUNT_MUST_BE_ONE,
> ++	LDNS_STATUS_QUERY_DID_NOT_MATCH
> + };
> + typedef enum ldns_enum_status ldns_status;
> + 
> +diff --git a/net.c b/net.c
> +index 8fc9c8c4..100226d4 100644
> +--- a/net.c
> ++++ b/net.c
> +@@ -441,6 +441,50 @@ ldns_udp_bgsend2(ldns_buffer *qbin,
> + 	return ldns_udp_bgsend_from(qbin, to, tolen, NULL, 0, timeout);
> + }
> + 
> ++/** helper sockaddr compare function. returns -1, 0 or 1. */
> ++static int
> ++ldns_sockaddr_cmp(const struct sockaddr_storage* addr1, socklen_t len1,
> ++	const struct sockaddr_storage* addr2, socklen_t len2)
> ++{
> ++	struct sockaddr_in* p1_in = (struct sockaddr_in*)addr1;
> ++	struct sockaddr_in* p2_in = (struct sockaddr_in*)addr2;
> ++	struct sockaddr_in6* p1_in6 = (struct sockaddr_in6*)addr1;
> ++	struct sockaddr_in6* p2_in6 = (struct sockaddr_in6*)addr2;
> ++	if(len1 < len2)
> ++		return -1;
> ++	if(len1 > len2)
> ++		return 1;
> ++	assert(len1 == len2);
> ++	if( p1_in->sin_family < p2_in->sin_family)
> ++		return -1;
> ++	if( p1_in->sin_family > p2_in->sin_family)
> ++		return 1;
> ++	assert( p1_in->sin_family == p2_in->sin_family );
> ++	/* compare ip4 */
> ++	if( p1_in->sin_family == AF_INET ) {
> ++		/* just order it, ntohs not required */
> ++		if(p1_in->sin_port < p2_in->sin_port)
> ++			return -1;
> ++		if(p1_in->sin_port > p2_in->sin_port)
> ++			return 1;
> ++		assert(p1_in->sin_port == p2_in->sin_port);
> ++		return memcmp(&p1_in->sin_addr, &p2_in->sin_addr,
> ++			sizeof(p1_in->sin_addr));
> ++	} else if (p1_in6->sin6_family == AF_INET6) {
> ++		/* just order it, ntohs not required */
> ++		if(p1_in6->sin6_port < p2_in6->sin6_port)
> ++			return -1;
> ++		if(p1_in6->sin6_port > p2_in6->sin6_port)
> ++			return 1;
> ++		assert(p1_in6->sin6_port == p2_in6->sin6_port);
> ++		return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr,
> ++			sizeof(p1_in6->sin6_addr));
> ++	} else {
> ++		/* eek unknown type, perform this comparison for sanity. */
> ++		return memcmp(addr1, addr2, len1);
> ++	}
> ++}
> ++
> + static ldns_status
> + ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin,
> + 		const struct sockaddr_storage *to  , socklen_t tolen,
> +@@ -449,6 +493,8 @@ ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin,
> + {
> + 	int sockfd;
> + 	uint8_t *answer;
> ++	struct sockaddr_storage reply_addr;
> ++	socklen_t reply_addr_len;
> + 
> + 	sockfd = ldns_udp_bgsend_from(qbin, to, tolen, from, fromlen, timeout);
> + 
> +@@ -467,13 +513,21 @@ ldns_udp_send_from(uint8_t **result, ldns_buffer *qbin,
> +          * but returns a 'NETWORK_ERROR' much like a timeout. */
> +         ldns_sock_nonblock(sockfd);
> + 
> +-	answer = ldns_udp_read_wire(sockfd, answer_size, NULL, NULL);
> ++	reply_addr_len = sizeof(reply_addr);
> ++	memset(&reply_addr, 0, reply_addr_len);
> ++	answer = ldns_udp_read_wire(sockfd, answer_size, &reply_addr,
> ++		&reply_addr_len);
> + 	close_socket(sockfd);
> + 
> + 	if (!answer) {
> + 		/* oops */
> + 		return LDNS_STATUS_NETWORK_ERR;
> + 	}
> ++	/* Check that the reply came from the to addr. */
> ++	if(ldns_sockaddr_cmp(to, tolen, &reply_addr, reply_addr_len) != 0) {
> ++		free(answer);
> ++		return LDNS_STATUS_NETWORK_ERR;
> ++	}
> + 
> + 	*result = answer;
> + 	return LDNS_STATUS_OK;
> +@@ -512,6 +566,10 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf
> + 
> + 	assert(r != NULL);
> + 
> ++	/* The query should at least have one question */
> ++	if(ldns_buffer_limit(qb) < 6 || ldns_buffer_read_u16_at(qb, 4) != 1)
> ++		return LDNS_STATUS_QDCOUNT_MUST_BE_ONE;
> ++
> + 	status = LDNS_STATUS_OK;
> + 	rtt = ldns_resolver_rtt(r);
> + 	ns_array = ldns_resolver_nameservers(r);
> +@@ -599,6 +657,16 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf
> + 			ldns_resolver_set_nameserver_rtt(r, i, LDNS_RESOLV_RTT_INF);
> + 			status = send_status;
> + 		}
> ++		if(reply_bytes && ldns_buffer_limit(qb) >= 2) {
> ++			uint16_t txid = ldns_buffer_read_u16_at(qb, 0);
> ++			if(reply_size < 2 ||
> ++				ldns_read_uint16(reply_bytes) != txid) {
> ++				status = LDNS_STATUS_ID_DID_NOT_MATCH;
> ++				LDNS_FREE(reply_bytes);
> ++				reply_bytes = NULL;
> ++				reply_size = 0;
> ++			}
> ++		}
> + 		
> + 		/* obey the fail directive */
> + 		if (!reply_bytes) {
> +@@ -608,7 +676,7 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf
> + 					LDNS_FREE(src);
> + 				}
> + 				LDNS_FREE(ns);
> +-				return LDNS_STATUS_ERR;
> ++				return status ? status : LDNS_STATUS_ERR;
> + 			} else {
> + 				LDNS_FREE(ns);
> + 				continue;
> +@@ -670,6 +738,26 @@ ldns_send_buffer(ldns_pkt **result, ldns_resolver *r, ldns_buffer *qb, ldns_rdf
> + #endif /* HAVE_SSL */
> + 
> + 	LDNS_FREE(reply_bytes);
> ++	if (reply) {
> ++		ldns_pkt *query = NULL;
> ++
> ++		if(ldns_pkt_qdcount(reply) != 1) {
> ++			status = LDNS_STATUS_QDCOUNT_MUST_BE_ONE;
> ++			ldns_pkt_free(reply);
> ++			reply = NULL;
> ++
> ++		} else if(ldns_wire2pkt(&query
> ++		                , ldns_buffer_begin(qb)
> ++		                , ldns_buffer_position(qb)) != LDNS_STATUS_OK
> ++		|| ldns_pkt_qdcount(query) != 1
> ++		|| ldns_rr_compare(ldns_rr_list_rr(ldns_pkt_question(query),0)
> ++		                  ,ldns_rr_list_rr(ldns_pkt_question(reply),0))){
> ++			status = LDNS_STATUS_QUERY_DID_NOT_MATCH;
> ++			ldns_pkt_free(reply);
> ++			reply = NULL;
> ++		}
> ++		ldns_pkt_free(query);
> ++	}
> + 	if (result) {
> + 		*result = reply;
> + 	}
> +-- 
> +2.55.0
> +
> diff --git a/package/libldns/libldns.mk b/package/libldns/libldns.mk
> index 0740d09fab..81ac03b173 100644
> --- a/package/libldns/libldns.mk
> +++ b/package/libldns/libldns.mk
> @@ -23,6 +23,9 @@ LIBLDNS_CONF_OPTS = \
>  	--without-pyldns \
>  	--without-pyldnsx
>  
> +# 0001-fix-cve-2026-10846.patch
> +LIBLDNS_IGNORE_CVES += CVE-2026-10846
> +
>  ifeq ($(BR2_PACKAGE_LIBOPENSSL),y)
>  LIBLDNS_CONF_OPTS += --enable-dane-verify
>  else
> -- 
> 2.55.0
> 
> _______________________________________________
> buildroot mailing list
> buildroot at buildroot.org
> https://lists.buildroot.org/mailman/listinfo/buildroot


More information about the buildroot mailing list