edns0: bounds-check option length before memcmp in check_source()

The EDNS0 option iteration loop read a 2-byte len field from the
packet and used it directly in:
  opt.scope_netmask = p[3];          // requires len >= 4
  memcmp(p, &opt, len)               // reads len bytes from packet

Neither access was guarded against len exceeding the remaining RDATA.
A crafted DNS reply whose OPT RDATA contains a CLIENT_SUBNET option
with an oversized len field would cause memcmp to read past the end of
the packet buffer, leaking heap memory.

Risk: currently the only caller that reaches this branch is
forward.c:process_reply(), which (before the companion fix) passed an
OPT-RR-scoped plen that made skip_name() return NULL first, so the
unsafe code was never reached.  With that companion fix applied the
path is live: a rogue upstream server or on-path attacker can craft a
reply with a malformed CLIENT_SUBNET len and trigger the OOB read,
potentially leaking adjacent heap contents.

Fix: break out of the loop if the option body would extend past the
RDATA boundary.

Signed-off-by: Dominik <dl6er@dl6er.de>
This commit is contained in:
Dominik authored and Simon Kelley committed 2026-03-30 18:24:11 +01:00
1 parent 3ea905b98b
commit 0630a45ae1
1 file changed
+2
+2
View File
@@ -469,6 +469,8 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
{
GETSHORT(code, p);
GETSHORT(len, p);
if (i + 4 + len > rdlen)
break; /* malformed: option body extends beyond RDATA */
if (code == EDNS0_OPTION_CLIENT_SUBNET)
{
if (peer)