diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-12-09 18:41:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-12-10 13:51:44 -0500 |
commit | 1bf3751ec90cc3174e01f0d701e8449ce163d113 (patch) | |
tree | e625d411db647c59274e74d664c903bb5bb6b502 | |
parent | 5e1f54201cb481f40a04bc47e1bc8c093a189e23 (diff) |
ipv4: ip_check_defrag must not modify skb before unsharing
ip_check_defrag() might be called from af_packet within the
RX path where shared SKBs are used, so it must not modify
the input SKB before it has unshared it for defragmentation.
Use skb_copy_bits() to get the IP header and only pull in
everything later.
The same is true for the other caller in macvlan as it is
called from dev->rx_handler which can also get a shared SKB.
Reported-by: Eric Leblond <eric@regit.org>
Cc: stable@vger.kernel.org
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/ip_fragment.c | 19 |
1 files changed, 9 insertions, 10 deletions
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 448e68546827..8d5cc75dac88 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c | |||
@@ -707,28 +707,27 @@ EXPORT_SYMBOL(ip_defrag); | |||
707 | 707 | ||
708 | struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) | 708 | struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user) |
709 | { | 709 | { |
710 | const struct iphdr *iph; | 710 | struct iphdr iph; |
711 | u32 len; | 711 | u32 len; |
712 | 712 | ||
713 | if (skb->protocol != htons(ETH_P_IP)) | 713 | if (skb->protocol != htons(ETH_P_IP)) |
714 | return skb; | 714 | return skb; |
715 | 715 | ||
716 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | 716 | if (!skb_copy_bits(skb, 0, &iph, sizeof(iph))) |
717 | return skb; | 717 | return skb; |
718 | 718 | ||
719 | iph = ip_hdr(skb); | 719 | if (iph.ihl < 5 || iph.version != 4) |
720 | if (iph->ihl < 5 || iph->version != 4) | ||
721 | return skb; | 720 | return skb; |
722 | if (!pskb_may_pull(skb, iph->ihl*4)) | 721 | |
723 | return skb; | 722 | len = ntohs(iph.tot_len); |
724 | iph = ip_hdr(skb); | 723 | if (skb->len < len || len < (iph.ihl * 4)) |
725 | len = ntohs(iph->tot_len); | ||
726 | if (skb->len < len || len < (iph->ihl * 4)) | ||
727 | return skb; | 724 | return skb; |
728 | 725 | ||
729 | if (ip_is_fragment(ip_hdr(skb))) { | 726 | if (ip_is_fragment(&iph)) { |
730 | skb = skb_share_check(skb, GFP_ATOMIC); | 727 | skb = skb_share_check(skb, GFP_ATOMIC); |
731 | if (skb) { | 728 | if (skb) { |
729 | if (!pskb_may_pull(skb, iph.ihl*4)) | ||
730 | return skb; | ||
732 | if (pskb_trim_rcsum(skb, len)) | 731 | if (pskb_trim_rcsum(skb, len)) |
733 | return skb; | 732 | return skb; |
734 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); | 733 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); |