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