From: W.C.A. Wijngaards Date: Wed, 20 May 2026 08:18:23 +0000 (+0200) Subject: - Fix CVE-2026-41292, Parsing a long list of incoming EDNS options X-Git-Tag: release-1.25.1~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ef5ca84360934fa1e857ebc371d4b093aea6355d;p=thirdparty%2Funbound.git - Fix CVE-2026-41292, Parsing a long list of incoming EDNS options degrades performance. Thanks to GitHub user 'N0zoM1z0', also Qifan Zhang from Palo Alto Networks, for the report. --- diff --git a/doc/Changelog b/doc/Changelog index 66895692d..c823bcbd2 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -10,6 +10,9 @@ Griffiths from 'calif.io' for the report. - Fix CVE-2026-40622, "Ghost domain name" variant. Thanks to Qifan Zhang, Palo Alto Networks, for the report. + - Fix CVE-2026-41292, Parsing a long list of incoming EDNS options + degrades performance. Thanks to GitHub user 'N0zoM1z0', also Qifan + Zhang from Palo Alto Networks, for the report. 23 April 2026: Wouter - Merge #1441: Fix buffer overrun in diff --git a/util/data/msgparse.c b/util/data/msgparse.c index a38bed62e..9239f8fe3 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -53,6 +53,8 @@ #include "sldns/parseutil.h" #include "sldns/wire2str.h" +#define MAX_PARSED_EDNS_OPTIONS 100 + /** smart comparison of (compressed, valid) dnames from packet */ static int smart_compare(sldns_buffer* pkt, uint8_t* dnow, @@ -950,7 +952,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, struct comm_reply* repinfo, uint32_t now, struct regional* region, struct cookie_secrets* cookie_secrets) { - int nsid_seen = 0, cookie_seen = 0, padding_seen = 0; + int i = 0, nsid_seen = 0, cookie_seen = 0, padding_seen = 0; /* To respond with a Keepalive option, the client connection must have * received one message with a TCP Keepalive EDNS option, and that * option must have 0 length data. Subsequent messages sent on that @@ -970,7 +972,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, /* while still more options, and have code+len to read */ /* ignores partial content (i.e. rdata len 3) */ - while(rdata_len >= 4) { + while(rdata_len >= 4 && i < MAX_PARSED_EDNS_OPTIONS) { uint16_t opt_code = sldns_read_uint16(rdata_ptr); uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); uint8_t server_cookie[40]; @@ -1150,6 +1152,7 @@ parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, } rdata_ptr += opt_len; rdata_len -= opt_len; + i++; } return LDNS_RCODE_NOERROR; } @@ -1164,6 +1167,7 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, struct rrset_parse* found_prev = 0; size_t rdata_len; uint8_t* rdata_ptr; + int i = 0; /* since the class encodes the UDP size, we cannot use hash table to * find the EDNS OPT record. Scan the packet. */ while(rrset) { @@ -1223,7 +1227,7 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, /* while still more options, and have code+len to read */ /* ignores partial content (i.e. rdata len 3) */ - while(rdata_len >= 4) { + while(rdata_len >= 4 && i < MAX_PARSED_EDNS_OPTIONS) { uint16_t opt_code = sldns_read_uint16(rdata_ptr); uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); rdata_ptr += 4; @@ -1238,6 +1242,7 @@ parse_extract_edns_from_response_msg(struct msg_parse* msg, } rdata_ptr += opt_len; rdata_len -= opt_len; + i++; } /* ignore rrsigs */ return LDNS_RCODE_NOERROR;