VYPR
Critical severity9.8NVD Advisory· Published May 20, 2026· Updated May 20, 2026

CVE-2026-33278

CVE-2026-33278

Description

NLnet Labs Unbound 1.19.1 up to and including version 1.25.0 has a vulnerability in the DNSSEC validator that enables denial of service and possible remote code execution as a result of deep copying a data structure and erroneously overwriting a destination pointer. An adversary can exploit the vulnerability by controlling a malicious signed zone and querying a vulnerable Unbound. When DS sub-queries need to suspend validation due to NSEC3 computational budget exhaustion (introduced in Unbound 1.19.1), Unbound deep-copies response messages to preserve them across memory region teardown. A struct-assignment bug overwrites the destination's pointer with the source's pointer. After the sub-query region is freed, the resumed validator dereferences this dangling pointer, triggering a crash or potentially enabling arbitrary code execution. Unbound 1.25.1 contains a patch with a fix to preserve the correct pointer when deep copying the data structure.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A dangling-pointer bug in Unbound's DNSSEC validator, introduced in 1.19.1, allows denial-of-service or possible RCE via a crafted signed zone.

Vulnerability

A dangling-pointer vulnerability exists in the DNSSEC validator of NLnet Labs Unbound versions 1.19.1 through 1.25.0. When DS sub-queries must be suspended due to the NSEC3 computational budget exhaustion mechanism (introduced in 1.19.1), Unbound deep-copies response messages to preserve them across memory region teardown. A struct-assignment bug overwrites the destination's pointer with the source's pointer instead of correctly copying the underlying data. After the sub-query memory region is freed, the resumed validator dereferences this dangling pointer, leading to a crash or potentially arbitrary code execution. An adversary can exploit this by controlling a malicious signed zone and sending a query to a vulnerable Unbound server [1].

Exploitation

An attacker must control a signed DNS zone and be able to send queries to a vulnerable Unbound instance. No authentication or special network position beyond standard DNS query access is required. The attack triggers the NSEC3 computational budget exhaustion code path during validation of DS resource records, causing Unbound to invoke the buggy deep-copy routine. The resulting use-after-free can be leveraged to crash the process or, with careful heap manipulation, achieve remote code execution [1].

Impact

Successful exploitation results in denial of service (crash of the Unbound process) and, under favorable heap conditions, arbitrary code execution in the context of the Unbound daemon. This could allow an attacker to fully compromise the resolver, potentially disrupting DNS resolution for clients or pivoting to further attacks [1].

Mitigation

Unbound 1.25.1 contains the official fix. Users running 1.25.0 can apply patch patch_CVE-2026-33278_with.diff or the minimal patch_CVE-2026-33278.diff from the NLnet Labs advisory page [1]. Execute patch -p1 < patch.diff in the Unbound source directory and rebuild. All versions prior to 1.19.1 are not affected because the vulnerable code path was introduced in that version. No workaround other than patching or upgrading is available [1].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Nlnetlabs/Unboundinferred2 versions
    >=1.19.1,<=1.25.0+ 1 more
    • (no CPE)range: >=1.19.1,<=1.25.0
    • (no CPE)range: >=1.19.1 <=1.25.0

Patches

1
6a31e470f80a

- Fix CVE-2026-33278, Possible remote code execution during DNSSEC

https://github.com/NLnetLabs/unboundW.C.A. WijngaardsMay 20, 2026Fixed in release-1.25.1via llm-release-walk
6 files changed · +115 39
  • doc/Changelog+4 0 modified
    @@ -1,3 +1,7 @@
    +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.
    +
     23 April 2026: Wouter
     	- Merge #1441: Fix buffer overrun in
     	  doq_repinfo_retrieve_localaddr().
    
  • services/cache/dns.c+7 1 modified
    @@ -712,10 +712,16 @@ struct dns_msg*
     dns_msg_deepcopy_region(struct dns_msg* origin, struct regional* region)
     {
     	size_t i;
    +	struct ub_packed_rrset_key** saved_rrsets;
     	struct dns_msg* res = NULL;
    +	size_t rep_alloc_size = sizeof(struct reply_info)
    +		- sizeof(struct rrset_ref);  /* this is the size of res->rep
    +						allocated in gen_dns_msg() */
     	res = gen_dns_msg(region, &origin->qinfo, origin->rep->rrset_count);
     	if(!res) return NULL;
    -	*res->rep = *origin->rep;
    +	saved_rrsets = res->rep->rrsets; /* save rrsets alloc by gen_dns_msg */
    +	memcpy(res->rep, origin->rep, rep_alloc_size);
    +	res->rep->rrsets = saved_rrsets;
     	if(origin->rep->reason_bogus_str) {
     		res->rep->reason_bogus_str = regional_strdup(region,
     			origin->rep->reason_bogus_str);
    
  • testdata/val_nsec3_iter_high.rpl+9 9 modified
    @@ -120,12 +120,12 @@ example.com.	IN SOA	ns.example.com. hostmaster.example.com. 2007090400 28800 720
     example.com.    3600    IN      RRSIG   SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854}
     
     ; closest encloser, H(example.com).
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCz/LkFOFcaQzVnyySW9ZoVUnxh7gIUdxyS9vqVDzo8pGhFU+3YogN2ZRk= ;{id = 2854}
    +b6fuorg741ufili49mg9j4328ig53sqg.example.com. NSEC3 1 1 123 aabb00123456bbccdd b6fuorg741ufili49mg9j4328ig53sqh SOA NS MX DNSKEY RRSIG
    +b6fuorg741ufili49mg9j4328ig53sqg.example.com.	3600	IN	RRSIG	NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AJlV5car66lq5f0ASx7W47A/OADkARAXzKt9ZLojXze+FWK9JjAX+eA=
     
    -; wildcard denial, H(*.example.com.) = 4f3cnt8cu22tngec382jj4gde4rb47ub
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHS+i/OB/V/gYmS1eQTXieXIXGjsAhQQ0Ql7TW/hsUklrb0DfoyhVPG95Q== ;{id = 2854}
    +; wildcard denial, H(*.example.com.) = k1a2vr9c269jummpru5d68qllbfmtdcb.
    +k1a2vr9c269jummpru5d68qllbfmtacb.example.com. NSEC3 1 1 123 aabb00123456bbccdd k1a2vr9c269jummpru5d68qllbfmtgcb A MX RRSIG
    +k1a2vr9c269jummpru5d68qllbfmtacb.example.com.	3600	IN	RRSIG	NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AARB9z4C1WZUI3WP3QAR7RJXFnN0qEBkEt8ocudxXzms4/7/2l6NNWc=
     
     ; next closer name, H(www.example.com.) = s1unhcti19bkdr98fegs0v46mbu3t4m3.
     s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG
    @@ -152,10 +152,10 @@ SECTION ANSWER
     SECTION AUTHORITY
     example.com.	IN SOA	ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
     example.com.    3600    IN      RRSIG   SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854}
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCz/LkFOFcaQzVnyySW9ZoVUnxh7gIUdxyS9vqVDzo8pGhFU+3YogN2ZRk= ;{id = 2854}
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHS+i/OB/V/gYmS1eQTXieXIXGjsAhQQ0Ql7TW/hsUklrb0DfoyhVPG95Q== ;{id = 2854}
    +b6fuorg741ufili49mg9j4328ig53sqg.example.com. NSEC3 1 1 123 aabb00123456bbccdd b6fuorg741ufili49mg9j4328ig53sqh SOA NS MX DNSKEY RRSIG
    +b6fuorg741ufili49mg9j4328ig53sqg.example.com.	3600	IN	RRSIG	NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AJlV5car66lq5f0ASx7W47A/OADkARAXzKt9ZLojXze+FWK9JjAX+eA=
    +k1a2vr9c269jummpru5d68qllbfmtacb.example.com. NSEC3 1 1 123 aabb00123456bbccdd k1a2vr9c269jummpru5d68qllbfmtgcb A MX RRSIG
    +k1a2vr9c269jummpru5d68qllbfmtacb.example.com.	3600	IN	RRSIG	NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. AARB9z4C1WZUI3WP3QAR7RJXFnN0qEBkEt8ocudxXzms4/7/2l6NNWc=
     s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG
     s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFFSH4klZKke48dYyddYDj17gjTS0AhUAltWicpFLWqW98/Af9Qlx70MH8o4= ;{id = 2854}
     
    
  • testdata/val_nx_nsec3_collision.rpl+12 19 modified
    @@ -89,6 +89,17 @@ ns.example.com.         IN      A       1.2.3.4
     ns.example.com. 3600    IN      RRSIG   A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
     ENTRY_END
     
    +ENTRY_BEGIN
    +MATCH opcode qtype qname
    +ADJUST copy_id
    +REPLY QR AA NOERROR
    +SECTION QUESTION
    +ns.example.com. IN AAAA
    +SECTION AUTHORITY
    +example.com.	IN SOA	ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
    +example.com.    3600    IN      RRSIG   SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854}
    +ENTRY_END
    +
     ; response to DNSKEY priming query
     ENTRY_BEGIN
     MATCH opcode qtype qname
    @@ -163,29 +174,11 @@ STEP 2 TIME_PASSES ELAPSE 0.05
     STEP 10 CHECK_ANSWER
     ENTRY_BEGIN
     MATCH all
    -REPLY QR RD RA DO NXDOMAIN
    +REPLY QR RD RA DO SERVFAIL
     SECTION QUESTION
     www.example.com. IN A
     SECTION ANSWER
     SECTION AUTHORITY
    -example.com.	IN SOA	ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
    -example.com.    3600    IN      RRSIG   SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854}
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3  1 1 123 aabb00123456bbccdd 6md8numosa4q9ugkffdo1bmm82t5j49s A RRSIG
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHndWrEEbuzezs/4lxeiMgEuUsUbAhR72gJgd/Zmhf80yoxCauw9k5OkCw== ;{id = 2854}
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 18 - 4f3cnt8cu22tngec382jj4gde4rb87ub A RRSIG
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 19 - 4f3cnt8cu22tngec382jj4gde4rb87ub A RRSIG
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFDRwji51WCXJg7W/3+Jx586af5qgAhQPxHegtzu1I/QbvCNrOOON05N1rw== ;{id = 2854}
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 18 -  s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 19 -  s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 00  s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 01  s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 02  s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 20 03  s1unhcti19bkdr98fegs0v46mbu3t4m4 A RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFDLy4GbR8ZaKHATVJGnGxzpsuq60AhQ1/pRbXi1ZbcYohzHgWzNC50fC5A== ;{id = 2854}
    -
     SECTION ADDITIONAL
     ENTRY_END
     
    
  • testdata/val_nx_nsec3_params.rpl+12 10 modified
    @@ -88,6 +88,17 @@ ns.example.com.         IN      A       1.2.3.4
     ns.example.com. 3600    IN      RRSIG   A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
     ENTRY_END
     
    +ENTRY_BEGIN
    +MATCH opcode qtype qname
    +ADJUST copy_id
    +REPLY QR AA NOERROR
    +SECTION QUESTION
    +ns.example.com. IN AAAA
    +SECTION AUTHORITY
    +example.com.	IN SOA	ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
    +example.com.    3600    IN      RRSIG   SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854}
    +ENTRY_END
    +
     ; response to DNSKEY priming query
     ENTRY_BEGIN
     MATCH opcode qtype qname
    @@ -144,20 +155,11 @@ ENTRY_END
     STEP 10 CHECK_ANSWER
     ENTRY_BEGIN
     MATCH all
    -REPLY QR RD RA DO NXDOMAIN
    +REPLY QR RD RA DO SERVFAIL
     SECTION QUESTION
     www.example.com. IN A
     SECTION ANSWER
     SECTION AUTHORITY
    -example.com.	IN SOA	ns.example.com. hostmaster.example.com. 2007090400 28800 7200 604800 18000
    -example.com.    3600    IN      RRSIG   SOA 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFCNGZ+/OfElYQMCZ77O9Lw9rhk7PAhUAmDcvTAst6Bq83qPq3r6c/Dm1nFc= ;{id = 2854}
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com. NSEC3 1 1 8 - 6md8numosa4q9ugkffdo1bmm82t5j49s SOA NS MX DNSKEY RRSIG
    -6md8numosa4q9ugkffdo1bmm82t5j39s.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCz/LkFOFcaQzVnyySW9ZoVUnxh7gIUdxyS9vqVDzo8pGhFU+3YogN2ZRk= ;{id = 2854}
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com. NSEC3 1 1 0 - 4f3cnt8cu22tngec382jj4gde4rb48ub A MX RRSIG
    -4f3cnt8cu22tngec382jj4gde4rb46ub.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MCwCFHS+i/OB/V/gYmS1eQTXieXIXGjsAhQQ0Ql7TW/hsUklrb0DfoyhVPG95Q== ;{id = 2854}
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com. NSEC3 1 1 123 aabb00123456bbccdd s1unhcti19bkdr98fegs0v46mbu3t4m4 A MX RRSIG
    -s1unhcti19bkdr98fegs0v46mbu3t4m2.example.com.   3600    IN      RRSIG   NSEC3 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFFSH4klZKke48dYyddYDj17gjTS0AhUAltWicpFLWqW98/Af9Qlx70MH8o4= ;{id = 2854}
    -
     SECTION ADDITIONAL
     ENTRY_END
     
    
  • validator/val_nsec3.c+71 0 modified
    @@ -456,6 +456,67 @@ filter_init(struct nsec3_filter* filter, struct ub_packed_rrset_key** list,
     	}
     }
     
    +/** Check if the NSEC3s have the same parameter set. */
    +static int
    +param_set_same(struct nsec3_filter* flt, char** reason)
    +{
    +	size_t rrsetnum;
    +	int rrnum;
    +	struct ub_packed_rrset_key* rrset;
    +	int have_params = 0;
    +	int first_algo = 0;
    +	size_t first_iter = 0;
    +	uint8_t* first_salt = NULL;
    +	size_t first_saltlen = 0;
    +
    +	/* If the NSEC3 parameter sets have distinct values, then they are
    +	 * from different NSEC3 chains, and we do not want that. */
    +	for(rrset=filter_first(flt, &rrsetnum, &rrnum); rrset;
    +		rrset=filter_next(flt, &rrsetnum, &rrnum)) {
    +		if(!have_params) {
    +			first_algo = nsec3_get_algo(rrset, rrnum);
    +			first_iter = nsec3_get_iter(rrset, rrnum);
    +			if(!nsec3_get_salt(rrset, rrnum, &first_salt,
    +				&first_saltlen)) {
    +				verbose(VERB_ALGO, "NSEC3 salt malformed");
    +				if(reason)
    +					*reason = "NSEC3 salt malformed";
    +				return 0;
    +			}
    +			have_params = 1;
    +		} else {
    +			uint8_t* salt = NULL;
    +			size_t saltlen = 0;
    +			if(nsec3_get_algo(rrset, rrnum) != first_algo) {
    +				verbose(VERB_ALGO, "NSEC3 algorithm mismatch");
    +				if(reason)
    +					*reason = "NSEC3 algorithm mismatch";
    +				return 0;
    +			}
    +			if(nsec3_get_iter(rrset, rrnum) != first_iter) {
    +				verbose(VERB_ALGO, "NSEC3 iterations mismatch");
    +				if(reason)
    +					*reason = "NSEC3 iterations mismatch";
    +				return 0;
    +			}
    +			if(!nsec3_get_salt(rrset, rrnum, &salt, &saltlen)) {
    +				verbose(VERB_ALGO, "NSEC3 salt malformed");
    +				if(reason)
    +					*reason = "NSEC3 salt malformed";
    +				return 0;
    +			}
    +			if(saltlen != first_saltlen ||
    +				memcmp(salt, first_salt, saltlen) != 0) {
    +				verbose(VERB_ALGO, "NSEC3 salt mismatch");
    +				if(reason)
    +					*reason = "NSEC3 salt mismatch";
    +				return 0;
    +			}
    +		}
    +	}
    +	return 1;
    +}
    +
     /**
      * Find max iteration count using config settings and key size
      * @param ve: validator environment with iteration count config settings.
    @@ -1192,6 +1253,8 @@ nsec3_prove_nameerror(struct module_env* env, struct val_env* ve,
     	filter_init(&flt, list, num, qinfo); /* init RR iterator */
     	if(!flt.zone)
     		return sec_status_bogus; /* no RRs */
    +	if(!param_set_same(&flt, NULL))
    +		return sec_status_bogus; /* nsec3 params from distinct chains*/
     	if(nsec3_iteration_count_high(ve, &flt, kkey))
     		return sec_status_insecure; /* iteration count too high */
     	log_nametypeclass(VERB_ALGO, "start nsec3 nameerror proof, zone", 
    @@ -1378,6 +1441,8 @@ nsec3_prove_nodata(struct module_env* env, struct val_env* ve,
     	filter_init(&flt, list, num, qinfo); /* init RR iterator */
     	if(!flt.zone)
     		return sec_status_bogus; /* no RRs */
    +	if(!param_set_same(&flt, NULL))
    +		return sec_status_bogus; /* nsec3 params from distinct chains*/
     	if(nsec3_iteration_count_high(ve, &flt, kkey))
     		return sec_status_insecure; /* iteration count too high */
     	return nsec3_do_prove_nodata(env, &flt, ct, qinfo, calc);
    @@ -1401,6 +1466,8 @@ nsec3_prove_wildcard(struct module_env* env, struct val_env* ve,
     	filter_init(&flt, list, num, qinfo); /* init RR iterator */
     	if(!flt.zone)
     		return sec_status_bogus; /* no RRs */
    +	if(!param_set_same(&flt, NULL))
    +		return sec_status_bogus; /* nsec3 params from distinct chains*/
     	if(nsec3_iteration_count_high(ve, &flt, kkey))
     		return sec_status_insecure; /* iteration count too high */
     
    @@ -1503,6 +1570,8 @@ nsec3_prove_nods(struct module_env* env, struct val_env* ve,
     		*reason = "no NSEC3 records";
     		return sec_status_bogus; /* no RRs */
     	}
    +	if(!param_set_same(&flt, reason))
    +		return sec_status_bogus; /* nsec3 params from distinct chains*/
     	if(nsec3_iteration_count_high(ve, &flt, kkey))
     		return sec_status_insecure; /* iteration count too high */
     
    @@ -1596,6 +1665,8 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve,
     	filter_init(&flt, list, num, qinfo); /* init RR iterator */
     	if(!flt.zone)
     		return sec_status_bogus; /* no RRs */
    +	if(!param_set_same(&flt, NULL))
    +		return sec_status_bogus; /* nsec3 params from distinct chains*/
     	if(nsec3_iteration_count_high(ve, &flt, kkey))
     		return sec_status_insecure; /* iteration count too high */
     
    

Vulnerability mechanics

Root cause

"A struct-assignment in dns_msg_deepcopy_region overwrites the destination's rrsets pointer with the source's pointer, causing a use-after-free when the source's memory region is freed."

Attack vector

An attacker controls a malicious signed DNS zone and sends a query to a vulnerable Unbound resolver (1.19.1 through 1.25.0). When DNSSEC validation encounters NSEC3 records and the computational budget is exhausted, Unbound deep-copies the response message via dns_msg_deepcopy_region to preserve it across memory region teardown. The struct-assignment bug in that function [patch_id=793314] overwrites the destination's rrsets pointer with the source's pointer. After the sub-query region is freed, the resumed validator dereferences this dangling pointer, leading to a crash or potentially arbitrary code execution [CWE-416].

Affected code

The primary bug is in services/cache/dns.c in the function dns_msg_deepcopy_region, where a struct assignment overwrites the destination's rrsets pointer. The patch also adds param_set_same checks in validator/val_nsec3.c across five NSEC3 proof functions (nsec3_prove_nameerror, nsec3_prove_nodata, nsec3_prove_wildcard, nsec3_prove_nods, nsec3_prove_nxornodata) to reject mismatched NSEC3 parameter sets.

What the fix does

The patch in services/cache/dns.c replaces the direct struct assignment `*res->rep = *origin->rep` with a memcpy of only the fixed-size portion of reply_info, then restores the destination's rrsets pointer that was correctly allocated by gen_dns_msg [patch_id=793314]. This prevents the destination from inheriting the source's rrsets pointer, which would become dangling after the source region is freed. Additionally, the patch adds a param_set_same validation function in validator/val_nsec3.c that rejects NSEC3 proofs when the RRset list contains records from distinct NSEC3 chains (mismatched algorithm, iteration count, or salt), preventing the vulnerable code path from being reached with inconsistent data.

Preconditions

  • networkAttacker must be able to send DNS queries to a vulnerable Unbound resolver.
  • inputAttacker must control a malicious signed DNS zone that returns NSEC3 records with distinct parameter sets (e.g., different salts or iteration counts).
  • configThe Unbound resolver must have DNSSEC validation enabled (default configuration).

Generated on May 20, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

1

News mentions

0

No linked articles in our index yet.