ip6: improve length checks for extension headers

Malformed packets could cause the extension header iteration code to
read from up to two bytes beyond the end of the packet's first pbuf.
This commit is contained in:
David van Moolenbroek 2017-01-09 20:30:24 +00:00 committed by Dirk Ziegelmeier
parent 7cedf7ae71
commit 9713baea55

View File

@ -598,15 +598,9 @@ netif_found:
switch (nexth) {
case IP6_NEXTH_HOPBYHOP:
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
/* Get next header type. */
nexth = *((u8_t *)p->payload);
/* Get the header length. */
hlen = 8 * (1 + *((u8_t *)p->payload + 1));
ip_data.current_ip_header_tot_len += hlen;
/* Skip over this header. */
if (hlen > p->len) {
/* Get and check the header length, while staying in packet bounds. */
if ((p->len < 8) ||
((hlen = 8 * (1 + *((u8_t *)p->payload + 1))) > p->len)) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
hlen, p->len));
@ -617,19 +611,20 @@ netif_found:
goto ip6_input_cleanup;
}
ip_data.current_ip_header_tot_len += hlen;
/* Get next header type. */
nexth = *((u8_t *)p->payload);
/* Skip over this header. */
pbuf_header(p, -(s16_t)hlen);
break;
case IP6_NEXTH_DESTOPTS:
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
/* Get next header type. */
nexth = *((u8_t *)p->payload);
/* Get the header length. */
hlen = 8 * (1 + *((u8_t *)p->payload + 1));
ip_data.current_ip_header_tot_len += hlen;
/* Skip over this header. */
if (hlen > p->len) {
/* Get and check the header length, while staying in packet bounds. */
if ((p->len < 8) ||
(hlen = 8 * (1 + *((u8_t *)p->payload + 1))) > p->len) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
hlen, p->len));
@ -640,19 +635,20 @@ netif_found:
goto ip6_input_cleanup;
}
ip_data.current_ip_header_tot_len += hlen;
/* Get next header type. */
nexth = *((u8_t *)p->payload);
/* Skip over this header. */
pbuf_header(p, -(s16_t)hlen);
break;
case IP6_NEXTH_ROUTING:
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
/* Get next header type. */
nexth = *((u8_t *)p->payload);
/* Get the header length. */
hlen = 8 * (1 + *((u8_t *)p->payload + 1));
ip_data.current_ip_header_tot_len += hlen;
/* Skip over this header. */
if (hlen > p->len) {
/* Get and check the header length, while staying in packet bounds. */
if ((p->len < 8) ||
(hlen = 8 * (1 + *((u8_t *)p->payload + 1))) > p->len) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
hlen, p->len));
@ -663,6 +659,12 @@ netif_found:
goto ip6_input_cleanup;
}
/* Get next header type. */
nexth = *((u8_t *)p->payload);
/* Skip over this header. */
ip_data.current_ip_header_tot_len += hlen;
pbuf_header(p, -(s16_t)hlen);
break;
@ -671,14 +673,8 @@ netif_found:
struct ip6_frag_hdr *frag_hdr;
LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
frag_hdr = (struct ip6_frag_hdr *)p->payload;
/* Get next header type. */
nexth = frag_hdr->_nexth;
/* Fragment Header length. */
hlen = 8;
ip_data.current_ip_header_tot_len += hlen;
/* Make sure this header fits in current pbuf. */
if (hlen > p->len) {
@ -692,6 +688,13 @@ netif_found:
goto ip6_input_cleanup;
}
ip_data.current_ip_header_tot_len += hlen;
frag_hdr = (struct ip6_frag_hdr *)p->payload;
/* Get next header type. */
nexth = frag_hdr->_nexth;
/* Offset == 0 and more_fragments == 0? */
if ((frag_hdr->_fragment_offset &
PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0) {