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`: