[9.20.17] fix: usr: Adding NSEC3 opt-out records could leave invalid records in chain

When creating an NSEC3 opt-out chain, a node in the chain could be removed too soon, causing the previous NSEC3 being unable to be found, resulting in invalid NSEC3 records to be left in the zone. This has been fixed.

Closes [#5671](https://gitlab.isc.org/isc-projects/bind9/-/issues/5671)

Backport of MR [!11340](https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/11340)

Merge branch 'backport-5671-fix-dbiterator-prev-9.20.17' into 'v9.20.17-release'

See merge request isc-private/bind9!892
This commit is contained in:
Andoni Duarte 2025-12-09 12:17:20 +00:00
commit 1d83a8ad46
2 changed files with 37 additions and 4 deletions

View file

@ -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 {

View file

@ -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))
{