added more out of range checks to dns_recv() (see bug #48924)

This commit is contained in:
sg 2016-08-30 22:17:36 +02:00
parent 9078f31544
commit 23147b0e21

View File

@ -601,11 +601,14 @@ dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addr
static u16_t static u16_t
dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset) dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
{ {
unsigned char n; int n;
u16_t response_offset = start_offset; u16_t response_offset = start_offset;
do { do {
n = pbuf_get_at(p, response_offset++); n = pbuf_try_get_at(p, response_offset++);
if (n < 0) {
return 0xFFFF;
}
/** @see RFC 1035 - 4.1.4. Message compression */ /** @see RFC 1035 - 4.1.4. Message compression */
if ((n & 0xc0) == 0xc0) { if ((n & 0xc0) == 0xc0) {
/* Compressed name: cannot be equal since we don't send them */ /* Compressed name: cannot be equal since we don't send them */
@ -613,7 +616,11 @@ dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
} else { } else {
/* Not compressed name */ /* Not compressed name */
while (n > 0) { while (n > 0) {
if ((*query) != pbuf_get_at(p, response_offset)) { int c = pbuf_try_get_at(p, response_offset);
if (c < 0) {
return 0xFFFF;
}
if ((*query) != (u8_t)c) {
return 0xFFFF; return 0xFFFF;
} }
++response_offset; ++response_offset;
@ -622,7 +629,11 @@ dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
} }
++query; ++query;
} }
} while (pbuf_get_at(p, response_offset) != 0); n = pbuf_try_get_at(p, response_offset);
if (n < 0) {
return 0xFFFF;
}
} while (n != 0);
return response_offset + 1; return response_offset + 1;
} }
@ -635,28 +646,34 @@ dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
* @return index to end of the name * @return index to end of the name
*/ */
static u16_t static u16_t
dns_parse_name(struct pbuf* p, u16_t query_idx) dns_skip_name(struct pbuf* p, u16_t query_idx)
{ {
unsigned char n; int n;
u16_t offset = query_idx;
do { do {
/* pbuf_get_at() returns 0 on out-of-bounds access, so we will terminate */ n = pbuf_try_get_at(p, offset++);
n = pbuf_get_at(p, query_idx++); if (n < 0) {
return 0xFFFF;
}
/** @see RFC 1035 - 4.1.4. Message compression */ /** @see RFC 1035 - 4.1.4. Message compression */
if ((n & 0xc0) == 0xc0) { if ((n & 0xc0) == 0xc0) {
/* Compressed name */ /* Compressed name: since we only want to skip it (not check it), stop here */
break; break;
} else { } else {
/* Not compressed name */ /* Not compressed name */
while (n > 0) { if (offset + n >= p->tot_len) {
++query_idx; return 0xFFFF;
--n;
} }
offset = (u16_t)(offset + n);
} }
/* pbuf_get_at() returns 0 on out-of-bounds access, so we will terminate */ n = pbuf_try_get_at(p, offset);
} while (pbuf_get_at(p, query_idx) != 0); if (n < 0) {
return 0xFFFF;
}
} while (n != 0);
return query_idx + 1; return offset + 1;
} }
/** /**
@ -1116,10 +1133,10 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
} else { } else {
while ((nanswers > 0) && (res_idx < p->tot_len)) { while ((nanswers > 0) && (res_idx < p->tot_len)) {
/* skip answer resource record's host name */ /* skip answer resource record's host name */
res_idx = dns_parse_name(p, res_idx); res_idx = dns_skip_name(p, res_idx);
/* dns_parse_name() may return an res_idx out of pbuf bounds. if (res_idx == 0xFFFF) {
* This will be caught in pbuf_copy_partial() below. goto memerr; /* ignore this packet */
*/ }
/* Check for IP address type and Internet class. Others are discarded. */ /* Check for IP address type and Internet class. Others are discarded. */
if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) { if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
@ -1168,6 +1185,9 @@ dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr,
#endif /* LWIP_IPV6 */ #endif /* LWIP_IPV6 */
} }
/* skip this answer */ /* skip this answer */
if ((int)(res_idx + htons(ans.len)) > 0xFFFF) {
goto memerr; /* ignore this packet */
}
res_idx += htons(ans.len); res_idx += htons(ans.len);
--nanswers; --nanswers;
} }