From 00eb64397313cc1e656b744f8a53e4808915f33a Mon Sep 17 00:00:00 2001
From: David van Moolenbroek <david@minix3.org>
Date: Mon, 9 Jan 2017 20:30:24 +0000
Subject: [PATCH 2/2] 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.
---
 src/core/ipv6/ip6.c | 65 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 34 insertions(+), 31 deletions(-)

diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c
index 18d1a13..aea0ca4 100644
--- a/src/core/ipv6/ip6.c
+++ b/src/core/ipv6/ip6.c
@@ -604,15 +604,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));
@@ -623,19 +617,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));
@@ -646,19 +641,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));
@@ -669,6 +665,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;
 
@@ -677,14 +679,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) {
@@ -698,6 +694,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) {
-- 
2.5.2

