diff -Naur dnsmasq-2.76.orig/src/dnsmasq.h dnsmasq-2.76/src/dnsmasq.h --- dnsmasq-2.76.orig/src/dnsmasq.h 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/dnsmasq.h 2017-09-26 11:46:52.543096582 -0700 @@ -1161,7 +1161,7 @@ u64 rand64(void); int legal_hostname(char *c); char *canonicalise(char *s, int *nomem); -unsigned char *do_rfc1035_name(unsigned char *p, char *sval); +unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit); void *safe_malloc(size_t size); void safe_pipe(int *fd, int read_noblock); void *whine_malloc(size_t size); diff -Naur dnsmasq-2.76.orig/src/dnssec.c dnsmasq-2.76/src/dnssec.c --- dnsmasq-2.76.orig/src/dnssec.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/dnssec.c 2017-09-26 11:46:12.100665576 -0700 @@ -2227,7 +2227,7 @@ p = (unsigned char *)(header+1); - p = do_rfc1035_name(p, name); + p = do_rfc1035_name(p, name, NULL); *p++ = 0; PUTSHORT(type, p); PUTSHORT(class, p); diff -Naur dnsmasq-2.76.orig/src/edns0.c dnsmasq-2.76/src/edns0.c --- dnsmasq-2.76.orig/src/edns0.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/edns0.c 2017-09-26 11:46:12.100665576 -0700 @@ -144,7 +144,7 @@ GETSHORT(len, p); /* malformed option, delete the whole OPT RR and start again. */ - if (i + len > rdlen) + if (i + 4 + len > rdlen) { rdlen = 0; is_last = 0; @@ -159,7 +159,7 @@ /* delete option if we're to replace it. */ p -= 4; rdlen -= len + 4; - memcpy(p, p+len+4, rdlen - i); + memmove(p, p+len+4, rdlen - i); PUTSHORT(rdlen, lenp); lenp -= 2; } @@ -192,7 +192,15 @@ !(p = skip_section(p, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), header, plen))) + { + free(buff); return plen; + } + if (p + 11 > limit) + { + free(buff); + return plen; /* Too big */ + } *p++ = 0; /* empty name */ PUTSHORT(T_OPT, p); PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */ @@ -204,6 +212,11 @@ /* Copy back any options */ if (buff) { + if (p + rdlen > limit) + { + free(buff); + return plen; /* Too big */ + } memcpy(p, buff, rdlen); free(buff); p += rdlen; @@ -217,8 +230,12 @@ /* Add new option */ if (optno != 0 && replace != 2) { + if (p + 4 > limit) + return plen; /* Too big */ PUTSHORT(optno, p); PUTSHORT(optlen, p); + if (p + optlen > limit) + return plen; /* Too big */ memcpy(p, opt, optlen); p += optlen; PUTSHORT(p - datap, lenp); diff -Naur dnsmasq-2.76.orig/src/forward.c dnsmasq-2.76/src/forward.c --- dnsmasq-2.76.orig/src/forward.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/forward.c 2017-09-26 11:46:12.100665576 -0700 @@ -1410,6 +1410,10 @@ udp_size = daemon->edns_pktsz; } + // Make sure the udp size is not smaller than the incoming message so that we + // do not underflow + if (udp_size < n) udp_size = n; + #ifdef HAVE_AUTH if (auth_dns) { diff -Naur dnsmasq-2.76.orig/src/option.c dnsmasq-2.76/src/option.c --- dnsmasq-2.76.orig/src/option.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/option.c 2017-09-26 11:46:12.100665576 -0700 @@ -1378,7 +1378,7 @@ } p = newp; - end = do_rfc1035_name(p + len, dom); + end = do_rfc1035_name(p + len, dom, NULL); *end++ = 0; len = end - p; free(dom); diff -Naur dnsmasq-2.76.orig/src/radv.c dnsmasq-2.76/src/radv.c --- dnsmasq-2.76.orig/src/radv.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/radv.c 2017-09-26 11:46:12.104665422 -0700 @@ -198,6 +198,9 @@ /* look for link-layer address option for logging */ if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz) { + if ((packet[9] * 8 - 2) * 3 - 1 >= MAXDNAME) { + return; + } print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2); mac = daemon->namebuff; } diff -Naur dnsmasq-2.76.orig/src/rfc1035.c dnsmasq-2.76/src/rfc1035.c --- dnsmasq-2.76.orig/src/rfc1035.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/rfc1035.c 2017-09-26 12:22:20.445298619 -0700 @@ -37,7 +37,7 @@ /* end marker */ { /* check that there are the correct no of bytes after the name */ - if (!CHECK_LEN(header, p, plen, extrabytes)) + if (!CHECK_LEN(header, p1 ? p1 : p, plen, extrabytes)) return 0; if (isExtract) @@ -485,6 +485,8 @@ { unsigned int i, len = *p1; unsigned char *p2 = p1; + if ((p1 + len - p) >= rdlen) + return 0; /* bad packet */ /* make counted string zero-term and sanitise */ for (i = 0; i < len; i++) { @@ -1058,12 +1060,21 @@ unsigned short usval; long lval; char *sval; +#define CHECK_LIMIT(size) \ + if (limit && p + (size) > (unsigned char*)limit) \ + { \ + va_end(ap); \ + goto truncated; \ + } if (truncp && *truncp) return 0; - + va_start(ap, format); /* make ap point to 1st unamed argument */ - + + /* nameoffset (1 or 2) + type (2) + class (2) + ttl (4) + 0 (2) */ + CHECK_LIMIT(12); + if (nameoffset > 0) { PUTSHORT(nameoffset | 0xc000, p); @@ -1072,7 +1083,13 @@ { char *name = va_arg(ap, char *); if (name) - p = do_rfc1035_name(p, name); + p = do_rfc1035_name(p, name, limit); + if (!p) + { + va_end(ap); + goto truncated; + } + if (nameoffset < 0) { PUTSHORT(-nameoffset | 0xc000, p); @@ -1093,6 +1110,7 @@ { #ifdef HAVE_IPV6 case '6': + CHECK_LIMIT(IN6ADDRSZ); sval = va_arg(ap, char *); memcpy(p, sval, IN6ADDRSZ); p += IN6ADDRSZ; @@ -1100,36 +1118,47 @@ #endif case '4': + CHECK_LIMIT(INADDRSZ); sval = va_arg(ap, char *); memcpy(p, sval, INADDRSZ); p += INADDRSZ; break; case 'b': + CHECK_LIMIT(1); usval = va_arg(ap, int); *p++ = usval; break; case 's': + CHECK_LIMIT(2); usval = va_arg(ap, int); PUTSHORT(usval, p); break; case 'l': + CHECK_LIMIT(4); lval = va_arg(ap, long); PUTLONG(lval, p); break; case 'd': - /* get domain-name answer arg and store it in RDATA field */ - if (offset) - *offset = p - (unsigned char *)header; - p = do_rfc1035_name(p, va_arg(ap, char *)); - *p++ = 0; + /* get domain-name answer arg and store it in RDATA field */ + if (offset) + *offset = p - (unsigned char *)header; + p = do_rfc1035_name(p, va_arg(ap, char *), limit); + if (!p) + { + va_end(ap); + goto truncated; + } + CHECK_LIMIT(1); + *p++ = 0; break; case 't': usval = va_arg(ap, int); + CHECK_LIMIT(usval); sval = va_arg(ap, char *); if (usval != 0) memcpy(p, sval, usval); @@ -1141,20 +1170,24 @@ usval = sval ? strlen(sval) : 0; if (usval > 255) usval = 255; + CHECK_LIMIT(usval + 1); *p++ = (unsigned char)usval; memcpy(p, sval, usval); p += usval; break; } +#undef CHECK_LIMIT va_end(ap); /* clean up variable argument pointer */ j = p - sav - 2; - PUTSHORT(j, sav); /* Now, store real RDLength */ + /* this has already been checked against limit before */ + PUTSHORT(j, sav); /* Now, store real RDLength */ /* check for overflow of buffer */ if (limit && ((unsigned char *)limit - p) < 0) { +truncated: if (truncp) *truncp = 1; return 0; diff -Naur dnsmasq-2.76.orig/src/rfc2131.c dnsmasq-2.76/src/rfc2131.c --- dnsmasq-2.76.orig/src/rfc2131.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/rfc2131.c 2017-09-26 11:46:12.112665112 -0700 @@ -155,7 +155,7 @@ for (offset = 0; offset < (len - 5); offset += elen + 5) { elen = option_uint(opt, offset + 4 , 1); - if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA) + if (option_uint(opt, offset, 4) == BRDBAND_FORUM_IANA && offset + elen + 5 <= len) { unsigned char *x = option_ptr(opt, offset + 5); unsigned char *y = option_ptr(opt, offset + elen + 5); @@ -2419,10 +2419,10 @@ if (fqdn_flags & 0x04) { - p = do_rfc1035_name(p, hostname); + p = do_rfc1035_name(p, hostname, NULL); if (domain) { - p = do_rfc1035_name(p, domain); + p = do_rfc1035_name(p, domain, NULL); *p++ = 0; } } diff -Naur dnsmasq-2.76.orig/src/rfc3315.c dnsmasq-2.76/src/rfc3315.c --- dnsmasq-2.76.orig/src/rfc3315.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/rfc3315.c 2017-09-26 11:46:12.112665112 -0700 @@ -206,6 +206,9 @@ /* RFC-6939 */ if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3))) { + if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) { + return 0; + } state->mac_type = opt6_uint(opt, 0, 2); state->mac_len = opt6_len(opt) - 2; memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len); @@ -213,6 +216,9 @@ for (opt = opts; opt; opt = opt6_next(opt, end)) { + if (opt6_ptr(opt, 0) + opt6_len(opt) >= end) { + return 0; + } int o = new_opt6(opt6_type(opt)); if (opt6_type(opt) == OPTION6_RELAY_MSG) { @@ -1472,10 +1478,10 @@ if ((p = expand(len + 2))) { *(p++) = state->fqdn_flags; - p = do_rfc1035_name(p, state->hostname); + p = do_rfc1035_name(p, state->hostname, NULL); if (state->send_domain) { - p = do_rfc1035_name(p, state->send_domain); + p = do_rfc1035_name(p, state->send_domain, NULL); *p = 0; } } diff -Naur dnsmasq-2.76.orig/src/util.c dnsmasq-2.76/src/util.c --- dnsmasq-2.76.orig/src/util.c 2016-05-18 07:51:54.000000000 -0700 +++ dnsmasq-2.76/src/util.c 2017-09-26 11:46:12.112665112 -0700 @@ -218,15 +218,20 @@ return ret; } -unsigned char *do_rfc1035_name(unsigned char *p, char *sval) +unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit) { int j; while (sval && *sval) { + if (limit && p + 1 > (unsigned char*)limit) + return p; + unsigned char *cp = p++; for (j = 0; *sval && (*sval != '.'); sval++, j++) { + if (limit && p + 1 > (unsigned char*)limit) + return p; #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE) *p++ = (*(++sval))-1;