netfilter: ipv6: fix crash caused by ipv6_find_hdr()

When calling:
    ipv6_find_hdr(skb, &thoff, -1, NULL)
on a fragmented packet, thoff would be left with a random
value causing callers to read random memory offsets with:
    skb_header_pointer(skb, thoff, ...)

Now we force ipv6_find_hdr() to return a failure in this case.
Calling:
  ipv6_find_hdr(skb, &thoff, -1, &fragoff)
will set fragoff as expected, and not return a failure.

Change-Id: Ib474e8a4267dd2b300feca325811330329684a88
Signed-off-by: JP Abgrall <jpa@google.com>
This commit is contained in:
JP Abgrall 2011-09-29 15:36:49 -07:00 committed by John Stultz
parent 564578ba45
commit 4a0851b8b5

View file

@ -166,15 +166,15 @@ EXPORT_SYMBOL_GPL(ipv6_find_tlv);
* to explore inner IPv6 header, eg. ICMPv6 error messages. * to explore inner IPv6 header, eg. ICMPv6 error messages.
* *
* If target header is found, its offset is set in *offset and return protocol * If target header is found, its offset is set in *offset and return protocol
* number. Otherwise, return -1. * number. Otherwise, return -ENOENT or -EBADMSG.
* *
* If the first fragment doesn't contain the final protocol header or * If the first fragment doesn't contain the final protocol header or
* NEXTHDR_NONE it is considered invalid. * NEXTHDR_NONE it is considered invalid.
* *
* Note that non-1st fragment is special case that "the protocol number * Note that non-1st fragment is special case that "the protocol number
* of last header" is "next header" field in Fragment header. In this case, * of last header" is "next header" field in Fragment header. In this case,
* *offset is meaningless and fragment offset is stored in *fragoff if fragoff * *offset is meaningless. If fragoff is not NULL, the fragment offset is
* isn't NULL. * stored in *fragoff; if it is NULL, return -EINVAL.
* *
* if flags is not NULL and it's a fragment, then the frag flag * if flags is not NULL and it's a fragment, then the frag flag
* IP6_FH_F_FRAG will be set. If it's an AH header, the * IP6_FH_F_FRAG will be set. If it's an AH header, the
@ -253,9 +253,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
if (target < 0 && if (target < 0 &&
((!ipv6_ext_hdr(hp->nexthdr)) || ((!ipv6_ext_hdr(hp->nexthdr)) ||
hp->nexthdr == NEXTHDR_NONE)) { hp->nexthdr == NEXTHDR_NONE)) {
if (fragoff) if (fragoff) {
*fragoff = _frag_off; *fragoff = _frag_off;
return hp->nexthdr; return hp->nexthdr;
} else {
return -EINVAL;
}
} }
return -ENOENT; return -ENOENT;
} }