act_csum: fix possible use after free
authorEric Dumazet <edumazet@google.com>
Fri, 12 Apr 2013 18:07:47 +0000 (11:07 -0700)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Apr 2013 19:25:41 +0000 (15:25 -0400)
tcf_csum_skb_nextlayer() / pskb_may_pull() can change skb->head, so we
must be careful not keeping pointers to previous headers.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Grégoire Baron <baronchon@n7mm.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/sched/act_csum.c

index 08fa1e8a4ca4a5a0d45e399103cd680622b870bd..3a4c0caa1f7de0fdfdb618bac9308aa32330c7f7 100644 (file)
@@ -166,15 +166,17 @@ static int tcf_csum_ipv4_igmp(struct sk_buff *skb,
        return 1;
 }
 
-static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h,
+static int tcf_csum_ipv6_icmp(struct sk_buff *skb,
                              unsigned int ihl, unsigned int ipl)
 {
        struct icmp6hdr *icmp6h;
+       const struct ipv6hdr *ip6h;
 
        icmp6h = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*icmp6h));
        if (icmp6h == NULL)
                return 0;
 
+       ip6h = ipv6_hdr(skb);
        icmp6h->icmp6_cksum = 0;
        skb->csum = csum_partial(icmp6h, ipl - ihl, 0);
        icmp6h->icmp6_cksum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
@@ -186,15 +188,17 @@ static int tcf_csum_ipv6_icmp(struct sk_buff *skb, struct ipv6hdr *ip6h,
        return 1;
 }
 
-static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph,
+static int tcf_csum_ipv4_tcp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl)
 {
        struct tcphdr *tcph;
+       const struct iphdr *iph;
 
        tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
        if (tcph == NULL)
                return 0;
 
+       iph = ip_hdr(skb);
        tcph->check = 0;
        skb->csum = csum_partial(tcph, ipl - ihl, 0);
        tcph->check = tcp_v4_check(ipl - ihl,
@@ -205,15 +209,17 @@ static int tcf_csum_ipv4_tcp(struct sk_buff *skb, struct iphdr *iph,
        return 1;
 }
 
-static int tcf_csum_ipv6_tcp(struct sk_buff *skb, struct ipv6hdr *ip6h,
+static int tcf_csum_ipv6_tcp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl)
 {
        struct tcphdr *tcph;
+       const struct ipv6hdr *ip6h;
 
        tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph));
        if (tcph == NULL)
                return 0;
 
+       ip6h = ipv6_hdr(skb);
        tcph->check = 0;
        skb->csum = csum_partial(tcph, ipl - ihl, 0);
        tcph->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
@@ -225,10 +231,11 @@ static int tcf_csum_ipv6_tcp(struct sk_buff *skb, struct ipv6hdr *ip6h,
        return 1;
 }
 
-static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph,
+static int tcf_csum_ipv4_udp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl, int udplite)
 {
        struct udphdr *udph;
+       const struct iphdr *iph;
        u16 ul;
 
        /*
@@ -242,6 +249,7 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, struct iphdr *iph,
        if (udph == NULL)
                return 0;
 
+       iph = ip_hdr(skb);
        ul = ntohs(udph->len);
 
        if (udplite || udph->check) {
@@ -276,10 +284,11 @@ ignore_obscure_skb:
        return 1;
 }
 
-static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h,
+static int tcf_csum_ipv6_udp(struct sk_buff *skb,
                             unsigned int ihl, unsigned int ipl, int udplite)
 {
        struct udphdr *udph;
+       const struct ipv6hdr *ip6h;
        u16 ul;
 
        /*
@@ -293,6 +302,7 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, struct ipv6hdr *ip6h,
        if (udph == NULL)
                return 0;
 
+       ip6h = ipv6_hdr(skb);
        ul = ntohs(udph->len);
 
        udph->check = 0;
@@ -328,7 +338,7 @@ ignore_obscure_skb:
 
 static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
 {
-       struct iphdr *iph;
+       const struct iphdr *iph;
        int ntkoff;
 
        ntkoff = skb_network_offset(skb);
@@ -353,19 +363,19 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
                break;
        case IPPROTO_TCP:
                if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
-                       if (!tcf_csum_ipv4_tcp(skb, iph, iph->ihl * 4,
+                       if (!tcf_csum_ipv4_tcp(skb, iph->ihl * 4,
                                               ntohs(iph->tot_len)))
                                goto fail;
                break;
        case IPPROTO_UDP:
                if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
-                       if (!tcf_csum_ipv4_udp(skb, iph, iph->ihl * 4,
+                       if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
                                               ntohs(iph->tot_len), 0))
                                goto fail;
                break;
        case IPPROTO_UDPLITE:
                if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
-                       if (!tcf_csum_ipv4_udp(skb, iph, iph->ihl * 4,
+                       if (!tcf_csum_ipv4_udp(skb, iph->ihl * 4,
                                               ntohs(iph->tot_len), 1))
                                goto fail;
                break;
@@ -377,7 +387,7 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags)
                    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
                        goto fail;
 
-               ip_send_check(iph);
+               ip_send_check(ip_hdr(skb));
        }
 
        return 1;
@@ -456,6 +466,7 @@ static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
                        ixhl = ipv6_optlen(ip6xh);
                        if (!pskb_may_pull(skb, hl + ixhl + ntkoff))
                                goto fail;
+                       ip6xh = (void *)(skb_network_header(skb) + hl);
                        if ((nexthdr == NEXTHDR_HOP) &&
                            !(tcf_csum_ipv6_hopopts(ip6xh, ixhl, &pl)))
                                goto fail;
@@ -464,25 +475,25 @@ static int tcf_csum_ipv6(struct sk_buff *skb, u32 update_flags)
                        break;
                case IPPROTO_ICMPV6:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_ICMP)
-                               if (!tcf_csum_ipv6_icmp(skb, ip6h,
+                               if (!tcf_csum_ipv6_icmp(skb,
                                                        hl, pl + sizeof(*ip6h)))
                                        goto fail;
                        goto done;
                case IPPROTO_TCP:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_TCP)
-                               if (!tcf_csum_ipv6_tcp(skb, ip6h,
+                               if (!tcf_csum_ipv6_tcp(skb,
                                                       hl, pl + sizeof(*ip6h)))
                                        goto fail;
                        goto done;
                case IPPROTO_UDP:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_UDP)
-                               if (!tcf_csum_ipv6_udp(skb, ip6h, hl,
+                               if (!tcf_csum_ipv6_udp(skb, hl,
                                                       pl + sizeof(*ip6h), 0))
                                        goto fail;
                        goto done;
                case IPPROTO_UDPLITE:
                        if (update_flags & TCA_CSUM_UPDATE_FLAG_UDPLITE)
-                               if (!tcf_csum_ipv6_udp(skb, ip6h, hl,
+                               if (!tcf_csum_ipv6_udp(skb, hl,
                                                       pl + sizeof(*ip6h), 1))
                                        goto fail;
                        goto done;