Bug 2439088 (CVE-2026-2291) - CVE-2026-2291 dnsmasq: dnsmasq: heap buffer overflow in cache via NAME_ESCAPE expansion
Summary: CVE-2026-2291 dnsmasq: dnsmasq: heap buffer overflow in cache via NAME_ESCAPE...
Keywords:
Status: NEW
Alias: CVE-2026-2291
Deadline: 2026-05-09
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security DevOps Team
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-02-11 20:06 UTC by OSIDB Bzimport
Modified: 2026-05-12 17:02 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-02-11 20:06:20 UTC
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`:


Note You need to log in before you can comment on or make changes to this bug.