| From 0d37fc5ca9cefe9d3e0db51c2733c6476a7996ec Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 24 Jun 2021 03:07:20 -0700 |
| Subject: ipv6: fix out-of-bound access in ip6_parse_tlv() |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| [ Upstream commit 624085a31c1ad6a80b1e53f686bf6ee92abbf6e8 ] |
| |
| 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> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/ipv6/exthdrs.c | 27 +++++++++++++-------------- |
| 1 file changed, 13 insertions(+), 14 deletions(-) |
| |
| diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c |
| index 15223451cd7f..4932dea9820b 100644 |
| --- a/net/ipv6/exthdrs.c |
| +++ b/net/ipv6/exthdrs.c |
| @@ -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; |
| -- |
| 2.30.2 |
| |