|
|
Message-ID: <c7bf1440-d2c7-493d-bbfc-2b814ccb7aa1@brad-house.com> Date: Mon, 6 Jul 2026 13:46:31 -0400 From: Brad House <brad@...d-house.com> To: oss-security@...ts.openwall.com Subject: c-ares 1.34.7 release: CVE-2026-33630, GHSA-pjmc-gx33-gc76, GHSA-jv8r-gqr9-68wj ## 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 ### Impact A 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. ### Patches Fixed 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 ### Workarounds Avoid 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 ### Summary c-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. ### Details Within 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. ### Impact A 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 concept A 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. ### Patch Name 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 ### Summary When 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. ### Impact A 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 concept A 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. ### Patch The 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.
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.