net: fix NULL dereferences in check_peer_redir()
[~shefty/rdma-dev.git] / net / ipv6 / route.c
index e8987da06667225e44dae0cbfaf7fc51490788d7..9e69eb0ec6dd48a96d92bcf59c2cd89ad5e8412a 100644 (file)
@@ -364,7 +364,7 @@ out:
 #ifdef CONFIG_IPV6_ROUTER_PREF
 static void rt6_probe(struct rt6_info *rt)
 {
-       struct neighbour *neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
+       struct neighbour *neigh;
        /*
         * Okay, this does not seem to be appropriate
         * for now, however, we need to check if it
@@ -373,8 +373,10 @@ static void rt6_probe(struct rt6_info *rt)
         * Router Reachability Probe MUST be rate-limited
         * to no more than one per minute.
         */
+       rcu_read_lock();
+       neigh = rt ? dst_get_neighbour(&rt->dst) : NULL;
        if (!neigh || (neigh->nud_state & NUD_VALID))
-               return;
+               goto out;
        read_lock_bh(&neigh->lock);
        if (!(neigh->nud_state & NUD_VALID) &&
            time_after(jiffies, neigh->updated + rt->rt6i_idev->cnf.rtr_probe_interval)) {
@@ -387,8 +389,11 @@ static void rt6_probe(struct rt6_info *rt)
                target = (struct in6_addr *)&neigh->primary_key;
                addrconf_addr_solict_mult(target, &mcaddr);
                ndisc_send_ns(rt->rt6i_dev, NULL, target, &mcaddr, NULL);
-       } else
+       } else {
                read_unlock_bh(&neigh->lock);
+       }
+out:
+       rcu_read_unlock();
 }
 #else
 static inline void rt6_probe(struct rt6_info *rt)
@@ -412,8 +417,11 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
 
 static inline int rt6_check_neigh(struct rt6_info *rt)
 {
-       struct neighbour *neigh = dst_get_neighbour(&rt->dst);
+       struct neighbour *neigh;
        int m;
+
+       rcu_read_lock();
+       neigh = dst_get_neighbour(&rt->dst);
        if (rt->rt6i_flags & RTF_NONEXTHOP ||
            !(rt->rt6i_flags & RTF_GATEWAY))
                m = 1;
@@ -430,6 +438,7 @@ static inline int rt6_check_neigh(struct rt6_info *rt)
                read_unlock_bh(&neigh->lock);
        } else
                m = 0;
+       rcu_read_unlock();
        return m;
 }
 
@@ -769,7 +778,7 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
                rt->rt6i_dst.plen = 128;
                rt->rt6i_flags |= RTF_CACHE;
                rt->dst.flags |= DST_HOST;
-               dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour(&ort->dst)));
+               dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_raw(&ort->dst)));
        }
        return rt;
 }
@@ -803,7 +812,7 @@ restart:
        dst_hold(&rt->dst);
        read_unlock_bh(&table->tb6_lock);
 
-       if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+       if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
                nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
        else if (!(rt->dst.flags & DST_HOST))
                nrt = rt6_alloc_clone(rt, &fl6->daddr);
@@ -1587,7 +1596,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src,
        dst_confirm(&rt->dst);
 
        /* Duplicate redirect: silently ignore. */
-       if (neigh == dst_get_neighbour(&rt->dst))
+       if (neigh == dst_get_neighbour_raw(&rt->dst))
                goto out;
 
        nrt = ip6_rt_copy(rt, dest);
@@ -1682,7 +1691,7 @@ again:
           1. It is connected route. Action: COW
           2. It is gatewayed route or NONEXTHOP route. Action: clone it.
         */
-       if (!dst_get_neighbour(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
+       if (!dst_get_neighbour_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP))
                nrt = rt6_alloc_cow(rt, daddr, saddr);
        else
                nrt = rt6_alloc_clone(rt, daddr);
@@ -2326,6 +2335,7 @@ static int rt6_fill_node(struct net *net,
        struct nlmsghdr *nlh;
        long expires;
        u32 table;
+       struct neighbour *n;
 
        if (prefix) {   /* user wants prefix routes only */
                if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
@@ -2414,8 +2424,11 @@ static int rt6_fill_node(struct net *net,
        if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
                goto nla_put_failure;
 
-       if (dst_get_neighbour(&rt->dst))
-               NLA_PUT(skb, RTA_GATEWAY, 16, &dst_get_neighbour(&rt->dst)->primary_key);
+       rcu_read_lock();
+       n = dst_get_neighbour(&rt->dst);
+       if (n)
+               NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
+       rcu_read_unlock();
 
        if (rt->dst.dev)
                NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
@@ -2608,12 +2621,14 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg)
 #else
        seq_puts(m, "00000000000000000000000000000000 00 ");
 #endif
+       rcu_read_lock();
        n = dst_get_neighbour(&rt->dst);
        if (n) {
                seq_printf(m, "%pi6", n->primary_key);
        } else {
                seq_puts(m, "00000000000000000000000000000000");
        }
+       rcu_read_unlock();
        seq_printf(m, " %08x %08x %08x %08x %8s\n",
                   rt->rt6i_metric, atomic_read(&rt->dst.__refcnt),
                   rt->dst.__use, rt->rt6i_flags,