Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: bridge: check len before accessing more nh data

In the while loop of br_nf_check_hbh_len(), similar to ip6_parse_tlv(),
before accessing 'nh[off + 1]', it should add a check 'len < 2'; and
before parsing IPV6_TLV_JUMBO, it should add a check 'optlen > len',
in case of overflows.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Reviewed-by: Simon Horman <simon.horman@corigine.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Reviewed-by: Aaron Conole <aconole@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>

authored by

Xin Long and committed by
Florian Westphal
a7f1a2f4 9ccff83b

+20 -25
+20 -25
net/bridge/br_netfilter_ipv6.c
··· 50 50 u32 pkt_len; 51 51 52 52 if (!pskb_may_pull(skb, off + 8)) 53 - goto bad; 53 + return -1; 54 54 nh = (unsigned char *)(ipv6_hdr(skb) + 1); 55 55 len = (nh[1] + 1) << 3; 56 56 57 57 if (!pskb_may_pull(skb, off + len)) 58 - goto bad; 58 + return -1; 59 59 nh = skb_network_header(skb); 60 60 61 61 off += 2; 62 62 len -= 2; 63 - 64 63 while (len > 0) { 65 - int optlen = nh[off + 1] + 2; 64 + int optlen; 66 65 67 - switch (nh[off]) { 68 - case IPV6_TLV_PAD1: 69 - optlen = 1; 70 - break; 66 + if (nh[off] == IPV6_TLV_PAD1) { 67 + off++; 68 + len--; 69 + continue; 70 + } 71 + if (len < 2) 72 + return -1; 73 + optlen = nh[off + 1] + 2; 74 + if (optlen > len) 75 + return -1; 71 76 72 - case IPV6_TLV_PADN: 73 - break; 74 - 75 - case IPV6_TLV_JUMBO: 77 + if (nh[off] == IPV6_TLV_JUMBO) { 76 78 if (nh[off + 1] != 4 || (off & 3) != 2) 77 - goto bad; 79 + return -1; 78 80 pkt_len = ntohl(*(__be32 *)(nh + off + 2)); 79 81 if (pkt_len <= IPV6_MAXPLEN || 80 82 ipv6_hdr(skb)->payload_len) 81 - goto bad; 83 + return -1; 82 84 if (pkt_len > skb->len - sizeof(struct ipv6hdr)) 83 - goto bad; 85 + return -1; 84 86 if (pskb_trim_rcsum(skb, 85 87 pkt_len + sizeof(struct ipv6hdr))) 86 - goto bad; 88 + return -1; 87 89 nh = skb_network_header(skb); 88 - break; 89 - default: 90 - if (optlen > len) 91 - goto bad; 92 - break; 93 90 } 94 91 off += optlen; 95 92 len -= optlen; 96 93 } 97 - if (len == 0) 98 - return 0; 99 - bad: 100 - return -1; 94 + 95 + return len ? -1 : 0; 101 96 } 102 97 103 98 int br_validate_ipv6(struct net *net, struct sk_buff *skb)