public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/bind] f43: Potential memory usage beyond configured limits (CVE-2026-11622)
@ 2026-08-26 9:29
0 siblings, 0 replies; only message in thread
From: @ 2026-08-26 9:29 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/bind
Branch : f43
Commit : 13bacbc0e50429b0b87f564645ba0cecb709a606
Author : Petr Menšík <pemensik@redhat.com>
Date : 2026-08-26T11:07:49+02:00
Stats : +283/-0 in 2 file(s)
URL : https://src.fedoraproject.org/rpms/bind/c/13bacbc0e50429b0b87f564645ba0cecb709a606?branch=f43
Log:
Potential memory usage beyond configured limits (CVE-2026-11622)
[9.18] [CVE-2026-11622] sec: usr: Prevent cache exhaustion under sustained attack
The cache memory can become exhausted with expired entries whose memory
is not released due to a sustained attack on the same DNS name that
prevents the cleanup. This has been fixed.
Closes: https://gitlab.isc.org/isc-projects/bind9/-/work_items/4760
---
diff --git a/bind-9.18-CVE-2026-11622.patch b/bind-9.18-CVE-2026-11622.patch
new file mode 100644
index 0000000..8600070
--- /dev/null
+++ b/bind-9.18-CVE-2026-11622.patch
@@ -0,0 +1,280 @@
+From d7e1f4495d6bac8c29b332e04e9b27140339375b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= <ondrej@sury.org>
+Date: Tue, 23 Jun 2026 10:59:38 +0200
+Subject: [PATCH] Make the dns_slabheaders in the cache reference counted
+
+Instead of only reference counting the enclosing qpcnode, add the
+reference counting directly to the slabheaders. The reference is
+incremented when an rdataset is bound to the header and decremented when
+the rdataset is disassociated, so a stale slabheader can be removed from
+the node's down chain as soon as its own reference count reaches zero,
+instead of waiting for the whole qpcnode to become unreferenced.
+
+Building on that, clean up the ancient headers eagerly: mark_ancient()
+is made idempotent, releases the header's own (container) reference and
+reaps the stale headers from the node's down chain as soon as their
+references reach zero. A header evicted over the per-name type limit is
+expired only after the new rdataset has been bound, so the bind's
+increment always precedes mark_ancient()'s decrement.
+
+Because a header can now be reclaimed independently of its node, the
+rdataset iterators must keep the header they are positioned on alive:
+each iterator takes a reference on its current header and releases it
+when it advances or is destroyed. Iteration otherwise stays lazy and
+re-reads the node on every step, so it still observes records added to
+the node while the iterator is live, as zone signing requires.
+
+The slab headers are shared with the zone databases, so the matching
+increment is added to every bind path. The noqname/closest proofs hand
+out rdatasets backed by bare slabs that have no header, so they are
+given a separate dns_rdataproof_rdatasetmethods that leaves the
+reference count untouched.
+
+(cherry picked from commit 2dabf117e1264fd13fb33096f87e78a039fd1c6c)
+(cherry picked from commit 231b1ca3edfb26389e1af39181aa6b4413e87ec4)
+---
+ bin/tests/system/reclimit/tests.sh | 4 +-
+ lib/dns/include/dns/rdataslab.h | 1 +
+ lib/dns/rbtdb.c | 77 +++++++++++++++++++++++++-----
+ 3 files changed, 69 insertions(+), 13 deletions(-)
+
+diff --git a/bin/tests/system/reclimit/tests.sh b/bin/tests/system/reclimit/tests.sh
+index c15225488f..55ccac7759 100644
+--- a/bin/tests/system/reclimit/tests.sh
++++ b/bin/tests/system/reclimit/tests.sh
+@@ -338,13 +338,13 @@ echo_i "checking that NXDOMAIN names over the max-types-per-name limit don't get
+
+ # Query for 10 NXDOMAIN types
+ for ntype in $(seq 65270 65279); do
+- check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1
++ check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 120 || ret=1
+ done
+ # Wait at least 1 second
+ sleep 1
+ # Query for 10 NXDOMAIN types again - these should not be cached
+ for ntype in $(seq 65270 65279); do
+- check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1
++ check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 120 || ret=1
+ done
+
+ if [ $ret -ne 0 ]; then echo_i "failed"; fi
+diff --git a/lib/dns/include/dns/rdataslab.h b/lib/dns/include/dns/rdataslab.h
+index 5729c004ca..6bd3b5997d 100644
+--- a/lib/dns/include/dns/rdataslab.h
++++ b/lib/dns/include/dns/rdataslab.h
+@@ -44,6 +44,7 @@
+ #include <stdbool.h>
+
+ #include <isc/lang.h>
++#include <isc/refcount.h>
+
+ #include <dns/types.h>
+
+diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c
+index 62bc97d783..0b8547950f 100644
+--- a/lib/dns/rbtdb.c
++++ b/lib/dns/rbtdb.c
+@@ -158,6 +158,7 @@ struct noqname {
+ };
+
+ typedef struct rdatasetheader {
++ isc_refcount_t references;
+ /*%
+ * Locked by the owning node's lock.
+ */
+@@ -1447,6 +1448,7 @@ init_rdataset(dns_rbtdb_t *rbtdb, rdatasetheader_t *h) {
+ h->heap_index = 0;
+ atomic_init(&h->attributes, 0);
+ atomic_init(&h->last_refresh_fail_ts, 0);
++ isc_refcount_init(&h->references, 1);
+
+ STATIC_ASSERT(sizeof(h->attributes) == 2,
+ "The .attributes field of rdatasetheader_t needs to be "
+@@ -1559,6 +1561,9 @@ rollback_node(dns_rbtnode_t *node, rbtdb_serial_t serial) {
+ }
+ }
+
++static void
++clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx, rdatasetheader_t *top);
++
+ static void
+ mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
+ uint_least16_t attributes = atomic_load_acquire(&header->attributes);
+@@ -1584,8 +1589,12 @@ mark_header_ancient(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) {
+ update_rrsetstats(rbtdb, header->type, attributes, false);
+ header->node->dirty = 1;
+
++ isc_refcount_decrement(&header->references);
++
+ /* Increment the stats counter for the ancient RRtype. */
+ update_rrsetstats(rbtdb, header->type, newattributes, true);
++
++ clean_stale_headers(rbtdb, rbtdb->common.mctx, header);
+ }
+
+ static void
+@@ -1621,12 +1630,19 @@ static void
+ clean_stale_headers(dns_rbtdb_t *rbtdb, isc_mem_t *mctx,
+ rdatasetheader_t *top) {
+ rdatasetheader_t *d, *down_next;
++ rdatasetheader_t *down_parent = top;
+
+ for (d = top->down; d != NULL; d = down_next) {
+ down_next = d->down;
+- free_rdataset(rbtdb, mctx, d);
++ d->next = down_parent;
++
++ if (isc_refcount_current(&d->references) == 0) {
++ free_rdataset(rbtdb, mctx, d);
++ down_parent->down = down_next;
++ } else {
++ down_parent = d;
++ }
+ }
+- top->down = NULL;
+ }
+
+ static void
+@@ -1642,6 +1658,7 @@ clean_cache_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node) {
+ for (current = node->data; current != NULL; current = top_next) {
+ top_next = current->next;
+ clean_stale_headers(rbtdb, mctx, current);
++ INSIST(current->down == NULL);
+ /*
+ * If current is nonexistent, ancient, or stale and
+ * we are not keeping stale, we can clean it up.
+@@ -3114,6 +3131,8 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
+ return;
+ }
+
++ isc_refcount_increment(&header->references);
++
+ dns__rbtnode_acquire(rbtdb, node, locktype);
+
+ INSIST(rdataset->methods == NULL); /* We must be disassociated. */
+@@ -6307,6 +6326,7 @@ add32(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode, const dns_name_t *nodename,
+ bool header_nx;
+ bool newheader_nx;
+ bool merge;
++ bool do_expireheader = false;
+ dns_rdatatype_t rdtype, covers;
+ rbtdb_rdatatype_t negtype, sigtype;
+ dns_trust_t trust;
+@@ -6856,6 +6876,7 @@ find_header:
+ }
+
+ if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
++ do_expireheader = true;
+ if (expireheader == NULL) {
+ expireheader = newheader;
+ }
+@@ -6869,15 +6890,6 @@ find_header:
+ */
+ expireheader = newheader;
+ }
+-
+- set_ttl(rbtdb, expireheader, 0);
+- mark_header_ancient(rbtdb, expireheader);
+- /*
+- * FIXME: In theory, we should mark the RRSIG
+- * and the header at the same time, but there is
+- * no direct link between those two header, so
+- * we would have to check the whole list again.
+- */
+ }
+ }
+ }
+@@ -6901,6 +6913,15 @@ find_header:
+ isc_rwlocktype_write, addedrdataset);
+ }
+
++ /*
++ * We need to delay the expiration of the header until we are bound to
++ * it to prevent decrement-then-increment on the header references.
++ */
++ if (do_expireheader) {
++ set_ttl(rbtdb, expireheader, 0);
++ mark_header_ancient(rbtdb, expireheader);
++ }
++
+ return ISC_R_SUCCESS;
+ }
+
+@@ -8692,6 +8713,12 @@ rdataset_disassociate(dns_rdataset_t *rdataset) {
+ dns_db_t *db = rdataset->private1;
+ dns_dbnode_t *node = rdataset->private2;
+
++ if (rdataset->methods == &rdataset_methods) {
++ rdatasetheader_t *header = rdataset->private3;
++ header--;
++ isc_refcount_decrement(&header->references);
++ }
++
+ detachnode(db, &node);
+ }
+
+@@ -8806,6 +8833,11 @@ rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+ dns_dbnode_t *cloned_node = NULL;
+
+ attachnode(db, node, &cloned_node);
++ if (source->methods == &rdataset_methods) {
++ rdatasetheader_t *header = source->private3;
++ header--;
++ isc_refcount_increment(&header->references);
++ }
+ INSIST(!ISC_LINK_LINKED(target, link));
+ *target = *source;
+ ISC_LINK_INIT(target, link);
+@@ -8969,6 +9001,11 @@ rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+
+ rbtiterator = (rbtdb_rdatasetiter_t *)(*iteratorp);
+
++ if (rbtiterator->current != NULL) {
++ isc_refcount_decrement(&rbtiterator->current->references);
++ rbtiterator->current = NULL;
++ }
++
+ if (rbtiterator->common.version != NULL) {
+ closeversion(rbtiterator->common.db,
+ &rbtiterator->common.version, false);
+@@ -9046,9 +9083,18 @@ rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+ }
+ }
+
++ if (header != NULL) {
++ isc_refcount_increment0(&header->references);
++ }
++
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
++ if (rbtiterator->current != NULL) {
++ isc_refcount_decrement(&rbtiterator->current->references);
++ rbtiterator->current = NULL;
++ }
++
+ rbtiterator->current = header;
+
+ if (header == NULL) {
+@@ -9140,9 +9186,18 @@ rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+ }
+ }
+
++ if (header != NULL) {
++ isc_refcount_increment0(&header->references);
++ }
++
+ NODE_UNLOCK(&rbtdb->node_locks[rbtnode->locknum].lock,
+ isc_rwlocktype_read);
+
++ if (rbtiterator->current != NULL) {
++ isc_refcount_decrement(&rbtiterator->current->references);
++ rbtiterator->current = NULL;
++ }
++
+ rbtiterator->current = header;
+
+ if (header == NULL) {
+--
+2.55.0
+
diff --git a/bind.spec b/bind.spec
index bd46400..19321b4 100644
--- a/bind.spec
+++ b/bind.spec
@@ -155,6 +155,8 @@ Patch40: bind-9.18-CVE-2026-10822-test.patch
Patch41: bind-9.18-CVE-2026-12617.patch
# https://gitlab.isc.org/isc-projects/bind9/commit/26437be900c68bbe0974ec9ab804c2894c19c419
Patch42: bind-9.18-CVE-2026-12617-test.patch
+# https://gitlab.isc.org/isc-projects/bind9/commit/14d23a4955c62efe974266a8e96a8c5d7caad040
+Patch43: bind-9.18-CVE-2026-11622.patch
%{?systemd_ordering}
# https://fedoraproject.org/wiki/Changes/RPMSuportForSystemdSysusers
@@ -959,6 +961,7 @@ fi;
- Potential wildcard CNAME RPZ policy bypass (CVE-2026-11331)
- Key Record using PRIVATEDNS algorithm may lead to exit (CVE-2026-10822)
- Record ordering based unexpected exit with CNAME or DNAME (CVE-2026-12617)
+- Potential memory usage beyond configured limits (CVE-2026-11622)
* Wed Jun 17 2026 Petr Menšík <pemensik@redhat.com> - 32:9.18.50-1
- Update to 9.18.50 (rhbz#2489833)
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-08-26 9:29 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-26 9:29 [rpms/bind] f43: Potential memory usage beyond configured limits (CVE-2026-11622)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox