]> git.ipfire.org Git - thirdparty/kernel/stable.git/blobdiff - net/ipv4/route.c
ipv4: add sanity checks in ipv4_link_failure()
[thirdparty/kernel/stable.git] / net / ipv4 / route.c
index 88ce038dd495dec1d34867eb40091c61141e9acb..6fdf1c195d8e3a0e32af0359794f798457a21cb3 100644 (file)
@@ -1183,25 +1183,39 @@ static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
        return dst;
 }
 
-static void ipv4_link_failure(struct sk_buff *skb)
+static void ipv4_send_dest_unreach(struct sk_buff *skb)
 {
        struct ip_options opt;
-       struct rtable *rt;
        int res;
 
        /* Recompile ip options since IPCB may not be valid anymore.
+        * Also check we have a reasonable ipv4 header.
         */
-       memset(&opt, 0, sizeof(opt));
-       opt.optlen = ip_hdr(skb)->ihl*4 - sizeof(struct iphdr);
+       if (!pskb_network_may_pull(skb, sizeof(struct iphdr)) ||
+           ip_hdr(skb)->version != 4 || ip_hdr(skb)->ihl < 5)
+               return;
 
-       rcu_read_lock();
-       res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL);
-       rcu_read_unlock();
+       memset(&opt, 0, sizeof(opt));
+       if (ip_hdr(skb)->ihl > 5) {
+               if (!pskb_network_may_pull(skb, ip_hdr(skb)->ihl * 4))
+                       return;
+               opt.optlen = ip_hdr(skb)->ihl * 4 - sizeof(struct iphdr);
 
-       if (res)
-               return;
+               rcu_read_lock();
+               res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL);
+               rcu_read_unlock();
 
+               if (res)
+                       return;
+       }
        __icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, &opt);
+}
+
+static void ipv4_link_failure(struct sk_buff *skb)
+{
+       struct rtable *rt;
+
+       ipv4_send_dest_unreach(skb);
 
        rt = skb_rtable(skb);
        if (rt)