oss-sec mailing list archives
c-ares 1.34.7 release: CVE-2026-33630, GHSA-pjmc-gx33-gc76, GHSA-jv8r-gqr9-68wj
From: Brad House <brad () brad-house com>
Date: Mon, 6 Jul 2026 13:46:31 -0400
## CVE-2026-33630: Use-after-free / double-free in c-ares query-completion handling, remotely triggerable via ares_getaddrinfo() over TCP
7.5 (High) https://github.com/c-ares/c-ares/security/advisories/GHSA-6wfj-rwm7-3542
### ImpactA use-after-free / double-free in c-ares' query-completion handling. The same flaw — a query's callback being invoked while the query is still linked in the channel's internal lookup structures — is present at multiple points in the resend/finish path (timeout handling, response handling, and query dispatch). If the query, or for `ares_getaddrinfo()` the owning `host_query`, is freed as a side effect of that callback, it is then accessed and/or freed a second time.
It is reachable in two ways:- **Application-triggered:** a callback that re-enters c-ares, for example by calling `ares_cancel()`. The callback frees the query and it is freed again after the callback returns.
- **Remotely, with no application cooperation:** via `ares_getaddrinfo()` over TCP. A malicious or on-path DNS server that returns a FORMERR without an OPT record (forcing an EDNS-downgrade retry), then a second response with the same query id, then resets the connection, causes `ares_getaddrinfo()`'s internal completion handler to re-enter (issuing a follow-on lookup whose send fails on the reset connection and completes immediately) and access a `host_query` that has already been freed. An attacker can force a client onto TCP by setting the truncation (TC) bit in a UDP response.
This was reproduced deterministically under AddressSanitizer (heap-use-after-free in the `ares_getaddrinfo()` completion handler) against v1.34.6 and the development branch, using only the public API and the standard system allocator.
The same pattern was previously addressed at one call site under CVE-2025-31498; CVE-2026-33630 covers the remaining occurrences of the pattern.
The consequence is memory corruption leading to a crash (denial of service); a use-after-free may have further impact depending on the allocator and build. Reliable code execution has not been demonstrated.
### PatchesFixed in c-ares 1.34.7. All deferred retries and completions are routed through a single iterative drain, and each query is fully detached from every lookup structure before its callback is invoked, so re-entrancy — whether from a caller's `ares_cancel()` or from c-ares' own internal follow-on lookups — can no longer free a query that is still in use. The recursive resend path that amplified the problem is also removed.
Fix commits:- `main`: https://github.com/c-ares/c-ares/commit/1fa3b86a0b8d18fe7b60f3228a01d770feb026bc - `v1.34` (backport, PR https://github.com/c-ares/c-ares/pull/1237): https://github.com/c-ares/c-ares/commit/d823199b688052dcdc1646f2ab4cb8c16b1c644a
### WorkaroundsAvoid calling `ares_cancel()` from within a query callback. This does not address the remotely-triggered path, which is driven entirely by DNS responses and has no reliable application-level workaround; upgrade to a fixed release, and prefer trusted resolvers reached over a trusted transport.
### Credit Reported independently by multiple researchers.## GHSA-pjmc-gx33-gc76: CPU-exhaustion denial of service via unbounded DNS name compression pointer chains
7.5 (High) https://github.com/c-ares/c-ares/security/advisories/GHSA-pjmc-gx33-gc76
### Summaryc-ares's DNS name decompression (`ares_dns_name_parse()`) enforced that every compression pointer must jump strictly backward, which prevents infinite loops, but placed no limit on the total number of pointer hops or on the assembled name length. A single crafted response could therefore make name decompression perform work quadratic in the message size.
### DetailsWithin a single ~64 KB TCP response an attacker can lay down a long descending chain of compression pointers (each pointing to the previous one) and then many resource records whose `NAME`/`RDATA` names each point at the end of that chain. Every such name is fully re-walked from the referenced position back to the start, so a message with a few thousand records referencing an ~8,000-deep chain drives tens of millions of pointer-follow operations. The reporter measured a single 65,524-byte response taking ~2.85 s to parse versus ~5.7 ms for a same-size benign response (~497x slowdown). Because c-ares runs on a single-threaded event loop, this stalls all resolution for the duration; responses arriving faster than they parse deny service entirely.
### ImpactA malicious or on-path DNS server responding to a victim's query can stall the c-ares event loop with individual crafted responses, degrading or denying DNS resolution for the application. Availability only -- no memory corruption or disclosure. Because one crafted response is sufficient to stall the single-threaded event loop, availability impact is scored High.
### Proof of conceptA crafted ~65 KB TCP response containing an ~8,176-pointer descending chain in a RAW_RR RDATA followed by ~3,510 NS records that reference the chain end for both NAME and NSDNAME (~57M total pointer follows). Full reproducer provided in the private report.
### PatchName parsing now caps the number of compression indirections at 128 (`ARES_MAX_INDIRS`, matching long-standing BIND behavior) and additionally enforces the RFC 1035 section 3.1 255-octet limit on the assembled (presentation) name length during decompression, so a name reached through a pointer chain can no longer be expanded without bound.
- `v1.34` branch: https://github.com/c-ares/c-ares/commit/5c8341ba6ff3a8e4e4dfd616f8ed0418838b8b7b (PR #1210, backport of #1164) - `main` branch: https://github.com/c-ares/c-ares/commit/f1288bbc70e9a1e0a77134c2382157e52d326aea (PR #1164)
### Workarounds None. Upgrade to a release containing the fix (1.34.7 or later). ### Credit Reported by Haruto Kimura (@HarutoKimura) of Stella.## GHSA-jv8r-gqr9-68wj: Memory-amplification denial of service via unvalidated DNS header record counts
5.3 (Moderate) https://github.com/c-ares/c-ares/security/advisories/GHSA-jv8r-gqr9-68wj
### SummaryWhen parsing a DNS response, c-ares pre-allocated per-section resource-record arrays sized directly from the attacker-controlled `ANCOUNT`, `NSCOUNT`, and `ARCOUNT` fields of the 12-byte DNS header, without first checking those counts against the number of bytes actually present in the message. A tiny response claiming the maximum record counts forced a large, disproportionate heap allocation.
### Details`ares_dns_parse()` is invoked from `process_answer()` before transaction-ID validation or question matching, so any received packet on a query's socket reaches the parser. The header counts (each up to 65535) drove `ares_dns_record_rr_prealloc()` -> `ares_array_set_size()`, which rounds the capacity up to the next power of two and allocates `capacity * sizeof(ares_dns_rr_t)`. With `ANCOUNT = NSCOUNT = ARCOUNT = 65535`, this allocated on the order of ~15 MB of heap for a 12-byte input (roughly a 1,000,000:1 amplification). The allocation is released when parsing subsequently fails, but a sustained stream of such responses produces allocator pressure that can degrade or deny service.
### ImpactA malicious or on-path DNS server (or an off-path spoofer able to land UDP responses on the query 4-tuple) can cause repeated large allocate/free cycles in any application using c-ares to resolve names. Availability only -- no memory corruption, information disclosure, or authentication bypass. Because the memory is transient (freed on parse failure) and impact materializes only under a sustained flood, availability impact is scored Low.
### Proof of conceptA 12-byte DNS response consisting of only a header with `QDCOUNT=0` and `ANCOUNT=NSCOUNT=ARCOUNT=0xFFFF`, parsed in a loop via `ares_dns_parse()`, produces multi-megabyte allocations per call (the reporter measured ~5,287 page faults per parse of the 12-byte packet). Full reproducer provided in the private report.
### PatchThe parser now rejects a response whose combined record count cannot possibly fit in the remaining message bytes (`total_rr_count > remaining_len / minimum_rr_wire_size`, minimum RR = 11 bytes) before any pre-allocation occurs.
- `v1.34` branch: https://github.com/c-ares/c-ares/commit/e47c203f91cd8b749c8736bc18d75a31ffdec8f4 (PR #1134) - `main` branch: https://github.com/c-ares/c-ares/commit/eaded4cb200b2a5f8d73f11021ff7c8d6968aaab (PR #1134)
### Workarounds None. Upgrade to a release containing the fix (1.34.7 or later). ### Credit Reported by Haruto Kimura (@HarutoKimura) of Stella.
Current thread:
- c-ares 1.34.7 release: CVE-2026-33630, GHSA-pjmc-gx33-gc76, GHSA-jv8r-gqr9-68wj Brad House (Jul 06)
