From 43cf27d0c219b74cbd39c2b95f7ba2b681310e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Fri, 5 Dec 2025 12:29:32 +0100 Subject: [PATCH 1/2] In dns_qpiter_{prev,next}, defer dereference_iter_node call dns_qpiter_{prev,next} requires the current iterator node to still be valid which might not always the case after dereference_iter_node was called. Currently, this is ensured via closeversion() mechanism, but it is not guaranteed to be true in the future. Move the call to dereference_iter_node to after the dns_qpiter_prev() and dns_qpiter_next() to prevent a possible use-after-free of the current iterator node. (cherry picked from commit 89478d95c39768793fa17dffaa2ca02cbd75f643) --- lib/dns/qpzone.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/dns/qpzone.c b/lib/dns/qpzone.c index 2c59fb7f98..9b36982348 100644 --- a/lib/dns/qpzone.c +++ b/lib/dns/qpzone.c @@ -4384,6 +4384,9 @@ dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db; + qpznode_t *node = NULL; + isc_rwlocktype_t nlocktype = isc_rwlocktype_none; + isc_rwlock_t *nlock = NULL; REQUIRE(qpdbiter->node != NULL); @@ -4391,7 +4394,12 @@ dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { return qpdbiter->result; } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); + /* + * Defer the release of the current node until we have the prev node + * from the QP tree. + */ + node = qpdbiter->node; + qpdbiter->node = NULL; result = dns_qpiter_prev(qpdbiter->current, NULL, (void **)&qpdbiter->node, NULL); @@ -4417,6 +4425,14 @@ dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { } } + /* + * We have the prev node, we can release the previous current. + */ + nlock = &qpdb->buckets[node->locknum].lock; + NODE_RDLOCK(nlock, &nlocktype); + qpznode_release(qpdb, node, 0, &nlocktype DNS__DB_FLARG_PASS); + NODE_UNLOCK(nlock, &nlocktype); + if (result == ISC_R_SUCCESS) { reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); } else { @@ -4432,6 +4448,9 @@ dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) { isc_result_t result; qpdb_dbiterator_t *qpdbiter = (qpdb_dbiterator_t *)iterator; qpzonedb_t *qpdb = (qpzonedb_t *)iterator->db; + qpznode_t *node = NULL; + isc_rwlocktype_t nlocktype = isc_rwlocktype_none; + isc_rwlock_t *nlock = NULL; REQUIRE(qpdbiter->node != NULL); @@ -4439,7 +4458,12 @@ dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) { return qpdbiter->result; } - dereference_iter_node(qpdbiter DNS__DB_FLARG_PASS); + /* + * Defer the release of the current node until we have the next node + * from the QP tree. + */ + node = qpdbiter->node; + qpdbiter->node = NULL; result = dns_qpiter_next(qpdbiter->current, NULL, (void **)&qpdbiter->node, NULL); @@ -4476,6 +4500,14 @@ dbiterator_next(dns_dbiterator_t *iterator DNS__DB_FLARG) { } } + /* + * We have the next node, we can release the previous current. + */ + nlock = &qpdb->buckets[node->locknum].lock; + NODE_RDLOCK(nlock, &nlocktype); + qpznode_release(qpdb, node, 0, &nlocktype DNS__DB_FLARG_PASS); + NODE_UNLOCK(nlock, &nlocktype); + if (result == ISC_R_SUCCESS) { reference_iter_node(qpdbiter DNS__DB_FLARG_PASS); } else { From eaa587ca309d38e5c65019ad02301a78398fe8b4 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 5 Dec 2025 14:25:02 +1100 Subject: [PATCH 2/2] In dbiterator_prev, dereference_iter_node was being called too soon dns_rbtnodechain_prev requires the current node to still be valid which was not always the case after dereference_iter_node was called. Move the call to dereference_iter_node to after the dns_rbtnodechain_prev to preserve the node. (cherry picked from commit b677d31fca2e54ca28318dd2b86e5cfe5bedb26c) --- lib/dns/rbtdb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 5f99080ce3..0a90e216c4 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -4675,11 +4675,12 @@ dbiterator_prev(dns_dbiterator_t *iterator DNS__DB_FLARG) { resume_iteration(rbtdbiter); } - dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); - name = dns_fixedname_name(&rbtdbiter->name); origin = dns_fixedname_name(&rbtdbiter->origin); result = dns_rbtnodechain_prev(rbtdbiter->current, name, origin); + + dereference_iter_node(rbtdbiter DNS__DB_FLARG_PASS); + if (rbtdbiter->current == &rbtdbiter->nsec3chain && (result == ISC_R_SUCCESS || result == DNS_R_NEWORIGIN)) {