ipv6: fix out-of-bound access in ip6_parse_tlv()
authorEric Dumazet <edumazet@google.com>
Thu, 24 Jun 2021 10:07:20 +0000 (03:07 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 Jun 2021 19:42:57 +0000 (12:42 -0700)
First problem is that optlen is fetched without checking
there is more than one byte to parse.

Fix this by taking care of IPV6_TLV_PAD1 before
fetching optlen (under appropriate sanity checks against len)

Second problem is that IPV6_TLV_PADN checks of zero
padding are performed before the check of remaining length.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Fixes: c1412fce7ecc ("net/ipv6/exthdrs.c: Strict PadN option checking")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/exthdrs.c

index 6f7da8f..26882e1 100644 (file)
@@ -135,18 +135,23 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
        len -= 2;
 
        while (len > 0) {
-               int optlen = nh[off + 1] + 2;
-               int i;
+               int optlen, i;
 
-               switch (nh[off]) {
-               case IPV6_TLV_PAD1:
-                       optlen = 1;
+               if (nh[off] == IPV6_TLV_PAD1) {
                        padlen++;
                        if (padlen > 7)
                                goto bad;
-                       break;
+                       off++;
+                       len--;
+                       continue;
+               }
+               if (len < 2)
+                       goto bad;
+               optlen = nh[off + 1] + 2;
+               if (optlen > len)
+                       goto bad;
 
-               case IPV6_TLV_PADN:
+               if (nh[off] == IPV6_TLV_PADN) {
                        /* RFC 2460 states that the purpose of PadN is
                         * to align the containing header to multiples
                         * of 8. 7 is therefore the highest valid value.
@@ -163,12 +168,7 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
                                if (nh[off + i] != 0)
                                        goto bad;
                        }
-                       break;
-
-               default: /* Other TLV code so scan list */
-                       if (optlen > len)
-                               goto bad;
-
+               } else {
                        tlv_count++;
                        if (tlv_count > max_count)
                                goto bad;
@@ -188,7 +188,6 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs,
                                return false;
 
                        padlen = 0;
-                       break;
                }
                off += optlen;
                len -= optlen;