Bug 2439088 (CVE-2026-2291)
| Summary: | CVE-2026-2291 dnsmasq: dnsmasq: heap buffer overflow in cache via NAME_ESCAPE expansion | ||
|---|---|---|---|
| Product: | [Other] Security Response | Reporter: | OSIDB Bzimport <bzimport> |
| Component: | vulnerability | Assignee: | Product Security DevOps Team <prodsec-dev> |
| Status: | NEW --- | QA Contact: | |
| Severity: | medium | Docs Contact: | |
| Priority: | medium | ||
| Version: | unspecified | CC: | rhel-process-autobot, security-response-team, watson-tool-maintainers |
| Target Milestone: | --- | Keywords: | Security |
| Target Release: | --- | ||
| Hardware: | All | ||
| OS: | Linux | ||
| Whiteboard: | |||
| Fixed In Version: | Doc Type: | --- | |
| Doc Text: |
A heap buffer overflow was discovered in dnsmasq's DNS cache. When processing DNS responses, dnsmasq expands certain characters into longer escape sequences, but the cache buffer is not sized to hold the expanded result. A specially crafted DNS response can overflow this buffer, potentially crashing the dnsmasq process or poisoning DNS cache records.
|
Story Points: | --- |
| Clone Of: | Environment: | ||
| Last Closed: | Type: | --- | |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
| Deadline: | 2026-05-09 | ||
Summary: `extract_name()` tracks name length in wire-format bytes and enforces `namelen < MAXDNAME`. But three byte values (0x00, 0x2E, 0x01) undergo 2-byte NAME_ESCAPE expansion in the presentation format. A wire-format name at the 1024-byte limit can expand to 2031 bytes in presentation format. The code accounted for this correctly when sizing `namebuff` -- the comment at `option.c:5942` explains the `(MAXDNAME * 2) + 1` allocation. But `union bigname` in `dnsmasq.h` is still sized at `MAXDNAME`: ```c // dnsmasq.h:481 union bigname { char name[MAXDNAME]; // 1025 bytes union bigname *next; }; ``` So the `strcpy` at `cache.c:760` can write up to 2031 bytes into a 1025-byte buffer: ```c strcpy(cache_get_name(new), name); // name comes from daemon->namebuff (2051 bytes) ``` A DNS response containing 16 labels of 63 NUL bytes (wire length 1024, passes the check) produces a presentation-format string of 2031 bytes -- a 1006-byte overflow. ## Trigger A PTR or CNAME response with NUL-byte-filled labels triggers it. NUL bytes in DNS labels are legal per RFC 2181 Section 11, and I confirmed Unbound and BIND forward them without sanitization. No special dnsmasq configuration is needed. ## Impact - **Crash:** A single malicious DNS response corrupts heap metadata. ASAN reports `WRITE of size 2032` at `cache.c:760`. Release builds crash on subsequent operations (`malloc(): corrupted top size`). - **Cache poisoning:** The overflow can overwrite an adjacent `bigname.name[]` with a target domain string, causing `cache_find_by_name()` to match the target domain against an entry with the attacker's IP. The `crec` metadata is untouched -- only the name changes. This bypasses TXID/port randomization since the attacker is the legitimate authoritative server for their own zone. ## Affected Versions The NAME_ESCAPE expansion was introduced in commit `cbe379a` (2015-04-21, "Handle domain names with '.' or /000 within labels"), first released in **v2.73**. That same commit upsized `namebuff` to `MAXDNAME * 2` with an explicit comment about the 2x expansion -- but `union bigname` was not updated. From v2.73 through v2.89, the expansion only ran in DNSSEC-enabled builds. In commit `638c7c4` (2023-03-23, "Add --cache-rr to enable caching of arbitrary RR types"), released in **v2.90**, the NAME_ESCAPE expansion was made **unconditional** -- it now runs in all builds regardless of DNSSEC configuration. The `namebuff` allocation was moved to `read_opts()` and unconditionally sized at `(MAXDNAME * 2) + 1`. - **v2.73 -- v2.89:** Vulnerable in DNSSEC-enabled builds only. - **v2.90 -- v2.92 (current):** Vulnerable in all builds, all configurations. ## ASAN Output Built v2.92 at `d8f66f4` with `-fsanitize=address -g -O0`. Triggered via a single PTR response containing 16 labels of 63 NUL bytes: ``` ==3387188==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x519000001888 WRITE of size 2032 at 0x519000001888 thread T0 #0 strcpy #1 really_insert cache.c:760 #2 cache_insert cache.c:626 #3 extract_addresses rfc1035.c:770 #4 process_reply forward.c:824 #5 return_reply forward.c:1426 #6 reply_query forward.c:1298 #7 check_dns_listeners dnsmasq.c:1905 #8 main dnsmasq.c:1297 0x519000001888 is located 0 bytes after 1032-byte region [0x519000001480,0x519000001888) allocated by thread T0 here: #0 calloc #1 whine_malloc util.c:346 #2 really_insert cache.c:731 ``` ## Patch Tested against v2.92 (`d8f66f4`): ASAN build survives the same payload that previously triggered the overflow. Applicable with `git am`: