[9.20] fix: dev: Fix a stack use-after-free in qpzone

In previous_closest_nsec(), a new qpreader was opened to search the NSEC
tree. It was possible for that to be used to update a QP iterator object
owned by the caller, and then be destroyed when the function returned.

This has been addressed by having the caller open the NSEC qpreader
instead.

Closes #5942

Merge branch '5942-qpiter-fix-bind-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!11956
This commit is contained in:
Evan Hunt 2026-05-06 00:01:08 +00:00
commit ddea991c07

View file

@ -269,6 +269,7 @@ typedef struct {
qpzonedb_t *qpdb;
qpz_version_t *version;
dns_qpread_t qpr;
dns_qpread_t nqpr;
uint32_t serial;
unsigned int options;
dns_qpchain_t chain;
@ -2969,7 +2970,6 @@ previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
dns_name_t *name, qpznode_t **nodep, dns_qpiter_t *nit,
bool *firstp) {
isc_result_t result;
dns_qpread_t qpr;
REQUIRE(nodep != NULL && *nodep == NULL);
REQUIRE(type == dns_rdatatype_nsec3 || firstp != NULL);
@ -2980,8 +2980,6 @@ previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
return result;
}
dns_qpmulti_query(search->qpdb->nsec, &qpr);
for (;;) {
if (*firstp) {
/*
@ -2989,8 +2987,8 @@ previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
* It is the first node sought in the NSEC tree.
*/
*firstp = false;
result = dns_qp_lookup(&qpr, name, NULL, nit, NULL,
NULL, NULL);
result = dns_qp_lookup(&search->nqpr, name, NULL, nit,
NULL, NULL, NULL);
INSIST(result != ISC_R_NOTFOUND);
if (result == ISC_R_SUCCESS) {
/*
@ -3044,7 +3042,6 @@ previous_closest_nsec(dns_rdatatype_t type, qpz_search_t *search,
}
}
dns_qpread_destroy(search->qpdb->nsec, &qpr);
return result;
}
@ -3398,6 +3395,7 @@ find(dns_db_t *db, const dns_name_t *name, dns_dbversion_t *version,
nsec3 = true;
} else {
dns_qpmulti_query(qpdb->tree, &search.qpr);
dns_qpmulti_query(qpdb->nsec, &search.nqpr);
}
/*
@ -3846,6 +3844,7 @@ tree_exit:
dns_qpread_destroy(qpdb->nsec3, &search.qpr);
} else {
dns_qpread_destroy(qpdb->tree, &search.qpr);
dns_qpread_destroy(qpdb->nsec, &search.nqpr);
}
/*