From d190532c319cf4e3b84000f4e389596f2af98fd4 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 13 May 2026 20:45:57 -0700 Subject: [PATCH 1/2] dns_rdataset_addnoqname() could find unsigned NSEC/NSEC3 The dns_rdatalist addnoqname() implementation searches for the first NSEC or NSEC3 record in a message, then for the first RRSIG covering that type in the same message. Previously, if no RRSIG for the type was found, the function accepted the unsigned record. Now, it will instead continue searching until an NSEC or NSEC3 that does have a matching signature is found. When this function is called from validated() in resolver.c, a non-success return code is now treated as an error instead of triggering an assertion failure. Fixes: isc-projects/bind9#5985 (cherry picked from commit 57cba571ee31311e54d8a11cb38094d439f04e09) --- lib/dns/rbtdb.c | 10 +++++++--- lib/dns/rdatalist.c | 33 ++++++++++++++++----------------- lib/dns/resolver.c | 4 +++- lib/ns/query.c | 3 +-- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index a341b007c7..dadb273bec 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -6906,7 +6906,7 @@ delegating_type(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, static isc_result_t addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, uint32_t maxrrperset, dns_rdataset_t *rdataset) { - struct noqname *noqname; + struct noqname *noqname = NULL; isc_mem_t *mctx = rbtdb->common.mctx; dns_name_t name; dns_rdataset_t neg, negsig; @@ -6918,7 +6918,9 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, dns_rdataset_init(&negsig); result = dns_rdataset_getnoqname(rdataset, &name, &neg, &negsig); - RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } noqname = isc_mem_get(mctx, sizeof(*noqname)); dns_name_init(&noqname->name, NULL); @@ -6944,7 +6946,9 @@ addnoqname(dns_rbtdb_t *rbtdb, rdatasetheader_t *newheader, cleanup: dns_rdataset_disassociate(&neg); dns_rdataset_disassociate(&negsig); - free_noqname(mctx, &noqname); + if (noqname != NULL) { + free_noqname(mctx, &noqname); + } return result; } diff --git a/lib/dns/rdatalist.c b/lib/dns/rdatalist.c index 971e4e656c..c6d77c75dd 100644 --- a/lib/dns/rdatalist.c +++ b/lib/dns/rdatalist.c @@ -192,6 +192,7 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { dns_rdataset_t *neg = NULL; dns_rdataset_t *negsig = NULL; dns_rdataset_t *rdset; + dns_rdataset_t *sigset; dns_ttl_t ttl; REQUIRE(rdataset != NULL); @@ -199,30 +200,27 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { for (rdset = ISC_LIST_HEAD(name->list); rdset != NULL; rdset = ISC_LIST_NEXT(rdset, link)) { - if (rdset->rdclass != rdataset->rdclass) { - continue; - } - if (rdset->type == dns_rdatatype_nsec || - rdset->type == dns_rdatatype_nsec3) + if (rdset->rdclass != rdataset->rdclass || + (rdset->type != dns_rdatatype_nsec && + rdset->type != dns_rdatatype_nsec3)) { - neg = rdset; + continue; } - } - if (neg == NULL) { - return ISC_R_NOTFOUND; - } - for (rdset = ISC_LIST_HEAD(name->list); rdset != NULL; - rdset = ISC_LIST_NEXT(rdset, link)) - { - if (rdset->type == dns_rdatatype_rrsig && - rdset->covers == neg->type) + for (sigset = ISC_LIST_HEAD(name->list); sigset != NULL; + sigset = ISC_LIST_NEXT(sigset, link)) { - negsig = rdset; + if (sigset->type == dns_rdatatype_rrsig && + sigset->covers == rdset->type) + { + neg = rdset; + negsig = sigset; + break; + } } } - if (negsig == NULL) { + if (neg == NULL || negsig == NULL) { return ISC_R_NOTFOUND; } /* @@ -238,6 +236,7 @@ isc__rdatalist_addnoqname(dns_rdataset_t *rdataset, const dns_name_t *name) { rdataset->ttl = neg->ttl = negsig->ttl = ttl; rdataset->attributes |= DNS_RDATASETATTR_NOQNAME; rdataset->private6 = name; + return ISC_R_SUCCESS; } diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 1326cb3428..a4efc9a847 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -5831,7 +5831,9 @@ validated(isc_task_t *task, isc_event_t *event) { result = dns_rdataset_addnoqname( vevent->rdataset, vevent->proofs[DNS_VALIDATOR_NOQNAMEPROOF]); - RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) { + goto noanswer_response; + } INSIST(vevent->sigrdataset != NULL); vevent->sigrdataset->ttl = vevent->rdataset->ttl; if (vevent->proofs[DNS_VALIDATOR_CLOSESTENCLOSER] != NULL) { diff --git a/lib/ns/query.c b/lib/ns/query.c index f7f0623713..f0239b20b9 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -7936,8 +7936,7 @@ query_addnoqnameproof(query_ctx_t *qctx) { goto cleanup; } - result = dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig); - RUNTIME_CHECK(result == ISC_R_SUCCESS); + CHECK(dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig)); query_addrrset(qctx, &fname, &neg, &negsig, dbuf, DNS_SECTION_AUTHORITY); From 8051316fde01f4a4368e8a4cba24e6d2d483e5ce Mon Sep 17 00:00:00 2001 From: RHEL Packaging Agent Date: Thu, 23 Jul 2026 10:26:08 +0000 Subject: [PATCH 2/2] Fix: replace CHECK macro with inline equivalent in query.c The CHECK macro is not defined in lib/ns/query.c (it is only defined locally in some other source files). Replace with inline equivalent that checks the result and goes to the cleanup label on failure. This preserves the security fix semantics: instead of crashing with RUNTIME_CHECK on error, gracefully jump to cleanup. --- lib/ns/query.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ns/query.c b/lib/ns/query.c index f0239b20b9..f265abfcef 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -7936,7 +7936,10 @@ query_addnoqnameproof(query_ctx_t *qctx) { goto cleanup; } - CHECK(dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig)); + result = dns_rdataset_getnoqname(qctx->noqname, fname, neg, negsig); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } query_addrrset(qctx, &fname, &neg, &negsig, dbuf, DNS_SECTION_AUTHORITY);