ipvs: Fix panic due to non-linear skb
authorPeter Christensen <pch@ordbogen.com>
Sat, 24 May 2014 19:40:12 +0000 (21:40 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 7 Jul 2014 01:54:15 +0000 (18:54 -0700)
commit f44a5f45f544561302e855e7bd104e5f506ec01b upstream.

Receiving a ICMP response to an IPIP packet in a non-linear skb could
cause a kernel panic in __skb_pull.

The problem was introduced in
commit f2edb9f7706dcb2c0d9a362b2ba849efe3a97f5e ("ipvs: implement
passive PMTUD for IPIP packets").

Signed-off-by: Peter Christensen <pch@ordbogen.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/netfilter/ipvs/ip_vs_core.c

index 21a3a475d7cd7d01bdc13223b8d56fe0518b7f26..663042e84e81ac7f89ffcbc6e458c9b475aacf8d 100644 (file)
@@ -1384,15 +1384,19 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
 
        if (ipip) {
                __be32 info = ic->un.gateway;
+               __u8 type = ic->type;
+               __u8 code = ic->code;
 
                /* Update the MTU */
                if (ic->type == ICMP_DEST_UNREACH &&
                    ic->code == ICMP_FRAG_NEEDED) {
                        struct ip_vs_dest *dest = cp->dest;
                        u32 mtu = ntohs(ic->un.frag.mtu);
+                       __be16 frag_off = cih->frag_off;
 
                        /* Strip outer IP and ICMP, go to IPIP header */
-                       __skb_pull(skb, ihl + sizeof(_icmph));
+                       if (pskb_pull(skb, ihl + sizeof(_icmph)) == NULL)
+                               goto ignore_ipip;
                        offset2 -= ihl + sizeof(_icmph);
                        skb_reset_network_header(skb);
                        IP_VS_DBG(12, "ICMP for IPIP %pI4->%pI4: mtu=%u\n",
@@ -1400,7 +1404,7 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
                        ipv4_update_pmtu(skb, dev_net(skb->dev),
                                         mtu, 0, 0, 0, 0);
                        /* Client uses PMTUD? */
-                       if (!(cih->frag_off & htons(IP_DF)))
+                       if (!(frag_off & htons(IP_DF)))
                                goto ignore_ipip;
                        /* Prefer the resulting PMTU */
                        if (dest) {
@@ -1419,12 +1423,13 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
                /* Strip outer IP, ICMP and IPIP, go to IP header of
                 * original request.
                 */
-               __skb_pull(skb, offset2);
+               if (pskb_pull(skb, offset2) == NULL)
+                       goto ignore_ipip;
                skb_reset_network_header(skb);
                IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
                        &ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
-                       ic->type, ic->code, ntohl(info));
-               icmp_send(skb, ic->type, ic->code, info);
+                       type, code, ntohl(info));
+               icmp_send(skb, type, code, info);
                /* ICMP can be shorter but anyways, account it */
                ip_vs_out_stats(cp, skb);