mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
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>
This commit is contained in:
committed by
Florian Westphal
parent
9ccff83b13
commit
a7f1a2f43e
@@ -50,54 +50,49 @@ static int br_nf_check_hbh_len(struct sk_buff *skb)
|
|||||||
u32 pkt_len;
|
u32 pkt_len;
|
||||||
|
|
||||||
if (!pskb_may_pull(skb, off + 8))
|
if (!pskb_may_pull(skb, off + 8))
|
||||||
goto bad;
|
return -1;
|
||||||
nh = (unsigned char *)(ipv6_hdr(skb) + 1);
|
nh = (unsigned char *)(ipv6_hdr(skb) + 1);
|
||||||
len = (nh[1] + 1) << 3;
|
len = (nh[1] + 1) << 3;
|
||||||
|
|
||||||
if (!pskb_may_pull(skb, off + len))
|
if (!pskb_may_pull(skb, off + len))
|
||||||
goto bad;
|
return -1;
|
||||||
nh = skb_network_header(skb);
|
nh = skb_network_header(skb);
|
||||||
|
|
||||||
off += 2;
|
off += 2;
|
||||||
len -= 2;
|
len -= 2;
|
||||||
|
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
int optlen = nh[off + 1] + 2;
|
int optlen;
|
||||||
|
|
||||||
switch (nh[off]) {
|
if (nh[off] == IPV6_TLV_PAD1) {
|
||||||
case IPV6_TLV_PAD1:
|
off++;
|
||||||
optlen = 1;
|
len--;
|
||||||
break;
|
continue;
|
||||||
|
}
|
||||||
|
if (len < 2)
|
||||||
|
return -1;
|
||||||
|
optlen = nh[off + 1] + 2;
|
||||||
|
if (optlen > len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
case IPV6_TLV_PADN:
|
if (nh[off] == IPV6_TLV_JUMBO) {
|
||||||
break;
|
|
||||||
|
|
||||||
case IPV6_TLV_JUMBO:
|
|
||||||
if (nh[off + 1] != 4 || (off & 3) != 2)
|
if (nh[off + 1] != 4 || (off & 3) != 2)
|
||||||
goto bad;
|
return -1;
|
||||||
pkt_len = ntohl(*(__be32 *)(nh + off + 2));
|
pkt_len = ntohl(*(__be32 *)(nh + off + 2));
|
||||||
if (pkt_len <= IPV6_MAXPLEN ||
|
if (pkt_len <= IPV6_MAXPLEN ||
|
||||||
ipv6_hdr(skb)->payload_len)
|
ipv6_hdr(skb)->payload_len)
|
||||||
goto bad;
|
return -1;
|
||||||
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
|
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
|
||||||
goto bad;
|
return -1;
|
||||||
if (pskb_trim_rcsum(skb,
|
if (pskb_trim_rcsum(skb,
|
||||||
pkt_len + sizeof(struct ipv6hdr)))
|
pkt_len + sizeof(struct ipv6hdr)))
|
||||||
goto bad;
|
return -1;
|
||||||
nh = skb_network_header(skb);
|
nh = skb_network_header(skb);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (optlen > len)
|
|
||||||
goto bad;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
off += optlen;
|
off += optlen;
|
||||||
len -= optlen;
|
len -= optlen;
|
||||||
}
|
}
|
||||||
if (len == 0)
|
|
||||||
return 0;
|
return len ? -1 : 0;
|
||||||
bad:
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int br_validate_ipv6(struct net *net, struct sk_buff *skb)
|
int br_validate_ipv6(struct net *net, struct sk_buff *skb)
|
||||||
|
Reference in New Issue
Block a user