[9.20] fix: dev: Refine resolver fetch loop detection

The resolver's fetch loop detection now triggers only when a new
fetch would join an already in-flight fetch that is also one of
its own ancestors, which is the actual loop condition.  Previously
the check ran against the original request before the fetch was
set up.

Backport of MR !12145

Merge branch 'backport-ondrej/improve-resolver-loop-detection-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!12146
This commit is contained in:
Ondřej Surý 2026-05-30 05:58:58 +02:00
commit 787faa02a7

View file

@ -982,10 +982,6 @@ set_stats(dns_resolver_t *res, isc_statscounter_t counter, uint64_t val) {
}
}
static bool
waiting_for_fetch(fetchctx_t *fctx, const dns_name_t *name,
dns_rdatatype_t type, const dns_name_t *domain);
static isc_result_t
valcreate(fetchctx_t *fctx, dns_message_t *message, dns_adbaddrinfo_t *addrinfo,
dns_name_t *name, dns_rdatatype_t type, dns_rdataset_t *rdataset,
@ -10792,15 +10788,15 @@ is_samedomain(const dns_name_t *domain1, const dns_name_t *domain2) {
}
static bool
waiting_for_fetch(fetchctx_t *fctx, const dns_name_t *name,
dns_rdatatype_t type, const dns_name_t *domain) {
while (fctx != NULL) {
if (type == fctx->type && !dns_name_compare(name, fctx->name)) {
if (is_samedomain(domain, fctx->domain)) {
return true;
}
waiting_for_fetch(const fetchctx_t *parent, const fetchctx_t *cur) {
for (const fetchctx_t *fctx = parent; fctx != NULL; fctx = fctx->parent)
{
if (cur->type == fctx->type &&
!dns_name_compare(cur->name, fctx->name) &&
is_samedomain(cur->domain, fctx->domain))
{
return true;
}
fctx = fctx->parent;
}
return false;
}
@ -10848,34 +10844,6 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
log_fetch(name, type);
/*
* This fetch loop detection enable to guard against loop scenarios
* where the DNSSEC is involved. See
* `4d307ac67a0e3f9831c9a4e66ac481e2f9ceebb5`. This is a complementary
* detection with the ADB lookup loop detection (in `findname()`).
*/
if (waiting_for_fetch(parent, name, type, domain)) {
if (isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) {
char namebuf[DNS_NAME_FORMATSIZE + 1];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE + 1] = { 0 };
dns_name_format(name, namebuf, sizeof(namebuf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
if (domain != NULL) {
dns_name_format(domain, domainbuf,
sizeof(domainbuf));
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(2),
"fetch loop detected resolving '%s/%s "
"(in '%s'?)",
namebuf, typebuf, domainbuf);
}
return DNS_R_LOOPDETECTED;
}
fetch = isc_mem_get(mctx, sizeof(*fetch));
*fetch = (dns_fetch_t){ 0 };
@ -10942,6 +10910,36 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
RUNTIME_CHECK(fctx != NULL);
/*
* This fetch loop detection enable to guard against loop scenarios
* where the DNSSEC is involved. See
* `4d307ac67a0e3f9831c9a4e66ac481e2f9ceebb5`. This is a complementary
* detection with the ADB lookup loop detection (in `findname()`).
*/
if (!new_fctx && waiting_for_fetch(parent, fctx)) {
if (isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) {
char namebuf[DNS_NAME_FORMATSIZE + 1];
char typebuf[DNS_RDATATYPE_FORMATSIZE];
char domainbuf[DNS_NAME_FORMATSIZE + 1] = { 0 };
dns_name_format(name, namebuf, sizeof(namebuf));
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
if (domain != NULL) {
dns_name_format(domain, domainbuf,
sizeof(domainbuf));
}
isc_log_write(dns_lctx, DNS_LOGCATEGORY_RESOLVER,
DNS_LOGMODULE_RESOLVER, ISC_LOG_DEBUG(2),
"fetch loop detected resolving '%s/%s "
"(in '%s'?)",
namebuf, typebuf, domainbuf);
}
result = DNS_R_LOOPDETECTED;
goto unlock;
}
if (fctx->depth > depth) {
fctx->depth = depth;
}