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
parent 5c35106592
commit 4f3824a38c

View File

@@ -2292,16 +2292,15 @@ static void __exit ip6_tables_fini(void)
* "No next header". * "No next header".
* *
* 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.
*
*/ */
int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
int target, unsigned short *fragoff) int target, unsigned short *fragoff)
@@ -2342,9 +2341,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;
} }