Introduction
A race condition in BIND 9's asynchronous SIG(0) signature validation can be triggered during a query flood, and it crashes the named process with a segmentation fault. Any organization that relies on BIND 9 for authoritative or recursive DNS is exposed to a remotely exploitable denial-of-service condition (scored CVSS 7.5). Reaching it requires no authentication, no user interaction, and no special configuration.
Technical Information
Root Cause: Raw Pointer in a Concurrent Context
The vulnerability lives in lib/dns/resolver.c, in the function rctx_respinit(). When BIND receives a DNS response containing a SIG(0) signature, it hands the cryptographic verification (for example, P-384 ECDSA) to an asynchronous worker thread via dns_message_checksig_async(). This async operation takes roughly 3 ms to complete. During that window, a flood of queries can exhaust the recursive-clients limit. When the limit is hit, BIND walks the call chain fctx_shutdown() → fctx_cancelqueries() → resquery_detach(), which drops the reference count on the resquery_t object to zero and frees it.
The problem is that the response context (rctx) held a raw pointer to this query object without bumping its reference count:
// VULNERABLE CODE in rctx_respinit() *rctx = (respctx_t){ .result = result, .query = query, /* raw pointer — no refcount bump */ .fctx = fctx, ... };
When the async signature check callback fires and invokes resquery_response_continue(), it dereferences rctx->query. If the concurrent cancellation path has already freed the query object, that is a dangling pointer dereference: a classic use after free (CWE-416) caused by a race condition (CWE-362).
On most systems, the result is a segmentation fault that crashes the named process. The vendor notes that if the memory from the discarded message has not been reused or reclaimed, the validation might proceed normally, but the behavior is formally undefined. ISC also states that arbitrary code execution from this improper data read is unlikely.
Attack Flow
An attacker needs no special privileges or prior access. Based on the vulnerability mechanics described in the advisory, the exploitation sequence would look like this:
- The attacker sends DNS messages signed with SIG(0) to a target BIND 9 resolver or authoritative server.
- At the same time, the attacker (or a separate botnet) floods the same server with a high volume of recursive queries to push it toward its
recursive-clientslimit. - When the limit is reached, BIND begins discarding in-flight queries. If a SIG(0)-signed message is among those discarded while its async cryptographic verification is still running, the race condition is triggered.
- The async callback dereferences the freed
resquery_tobject, causing a segmentation fault and crashing thenamedprocess.
Because the trigger condition closely resembles a standard query flood, this vulnerability could be folded into existing DDoS tooling with minimal effort.
Patch Information
ISC publicly disclosed CVE-2026-5947 on May 20, 2026 and released patched versions of BIND 9 at the same time. The fix is in BIND 9.20.23, BIND 9.21.22, and BIND Supported Preview Edition 9.20.23-S1. The closing commits are tracked in the ISC GitLab repository under issue GL #5819, with commit 9831f418 landing on the v9.20 branch and commit f6d19314 on the development branch.
The fix follows the ownership pattern already used for rctx->mctx, which relies on isc_mem_attach(). The patch replaces the raw pointer assignment with a reference-counted attach and adds a matching detach call at every exit point where rctx is freed. The researcher Naoki Wakamatsu proposed essentially the same solution in the original vulnerability report. Here is the core of the diff:
--- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ rctx_respinit() *rctx = (respctx_t){ .result = result, - .query = query, .fctx = fctx, .broken_type = badns_response, .retryopts = query->options }; + /* Attach so rctx holds a counted reference; detach when rctx is freed. */ + resquery_attach(query, &rctx->query); @@ resquery_response() cleanup: + resquery_detach(&rctx->query); isc_mem_putanddetach(&rctx->mctx, rctx, sizeof(*rctx)); @@ resquery_response_continue() cleanup: + resquery_detach(&rctx->query); isc_mem_putanddetach(&rctx->mctx, rctx, sizeof(*rctx));
Calling resquery_attach() increments the reference count on the resquery_t object. Even if fctx_cancelqueries() runs concurrently and drops its own reference, the object cannot be freed while the async SIG(0) verification is still in flight. The matching resquery_detach() at the cleanup label brings the refcount back down once the response context is done. This closes the race window completely.
The BIND 9.18.x ESV branch is not affected, because the async SIG(0) verification path (dns_message_checksig_async()) was introduced in commit 70ff4a3f on March 6, 2024, and first shipped in v9.20.0.
No workarounds are available. Administrators running any affected version must upgrade to the corresponding patched release.
Affected Systems and Versions
The following table summarizes affected, unaffected, and fixed versions:
| Category | Versions |
|---|---|
| Affected | 9.20.0 through 9.20.22 |
| Affected | 9.21.0 through 9.21.21 |
| Affected | 9.20.9-S1 through 9.20.22-S1 (Supported Preview Edition) |
| Not Affected | 9.18.28 through 9.18.49 |
| Not Affected | 9.18.28-S1 through 9.18.49-S1 |
| Fixed | 9.20.23, 9.21.22, 9.20.23-S1 |
Both authoritative servers and resolvers running affected versions are vulnerable. The vulnerability is triggered when the server processes SIG(0)-signed DNS messages under query flood conditions that push it to the recursive-clients limit.
Organizations running the 9.18.x branch do not need emergency patches for this CVE, though 9.18.x is approaching its end of life in Q2 2026.



