From ca509cb61109f557e51039ddeffb38ffcbf6de2e Mon Sep 17 00:00:00 2001 From: Dominik Date: Thu, 19 Mar 2026 22:11:06 +0100 Subject: [PATCH] dhcp-common: fix OOB reads in DHCPv6 option_string() decoders Two decoders in option_string() read beyond the end of the supplied option-value buffer when a malformed DHCPv6 option is received: 1. OT_RFC1035_NAME: after advancing i past the last label (i = l), val[i] was read at line 885 to decide whether to append a '.'. If the label ends exactly at opt_len, i == opt_len and val[i] is one byte past the option data. Fixed by guarding with i < opt_len before the read. 2. OT_CSTRING: the loop opened with while(1) and called GETSHORT (a 2-byte read) before checking that two bytes remained, so a 1- or 0-byte option value caused an immediate 1-byte OOB read. Additionally, if the length field exceeded the remaining option data the inner body-copy loop read past the end. Fixed by changing the loop condition to while(i+2 <= opt_len) and adding a check that the body fits before copying. Both reads land inside the received packet buffer (no crash), but they allow a DHCPv6 client to cause dnsmasq to read unrelated packet bytes and emit them in syslog output. Security: OOB reads of up to option-length bytes from a network-supplied DHCPv6 packet; reachable only with --log-dhcp enabled; no memory corruption, crash, or sensitive data disclosure beyond adjacent packet bytes. Signed-off-by: Dominik --- src/dhcp-common.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 59dcbcd..58d173d 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -892,24 +892,23 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, unsigned char *p; i = 0, j = 0; - while (1) + while (i + 2 <= opt_len) { p = &val[i]; GETSHORT(len, p); + if (i + 2 + len > opt_len) + break; /* malformed: body extends beyond option */ for (k = 0; k < len && j < buf_len; k++) { char c = *p++; if (isprint((unsigned char)c)) buf[j++] = c; } - i += len +2; - if (i >= opt_len) - break; - - if (j < buf_len) + i += len + 2; + if (i < opt_len && j < buf_len) buf[j++] = ','; } - } + } #endif else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0) {