net/ipv6: fix metrics leak
authorSabrina Dubroca <sd@queasysnail.net>
Mon, 30 Jul 2018 14:23:10 +0000 (16:23 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Jul 2018 16:45:57 +0000 (09:45 -0700)
Since commit d4ead6b34b67 ("net/ipv6: move metrics from dst to
rt6_info"), ipv6 metrics are shared and refcounted. rt6_set_from()
assigns the rt->from pointer and increases the refcount on from's
metrics. This reference is never released.

Introduce the fib6_metrics_release() helper and use it to release the
metrics.

Fixes: d4ead6b34b67 ("net/ipv6: move metrics from dst to rt6_info")
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/ip6_fib.c

index d212738e9d100d4e3270f9188466da6b8a3d186c..211a2d437b5650af28d1b0af9dffd060a530e934 100644 (file)
@@ -167,11 +167,22 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags)
        return f6i;
 }
 
+static void fib6_metrics_release(struct fib6_info *f6i)
+{
+       struct dst_metrics *m;
+
+       if (!f6i)
+               return;
+
+       m = f6i->fib6_metrics;
+       if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt))
+               kfree(m);
+}
+
 void fib6_info_destroy_rcu(struct rcu_head *head)
 {
        struct fib6_info *f6i = container_of(head, struct fib6_info, rcu);
        struct rt6_exception_bucket *bucket;
-       struct dst_metrics *m;
 
        WARN_ON(f6i->fib6_node);
 
@@ -201,9 +212,7 @@ void fib6_info_destroy_rcu(struct rcu_head *head)
        if (f6i->fib6_nh.nh_dev)
                dev_put(f6i->fib6_nh.nh_dev);
 
-       m = f6i->fib6_metrics;
-       if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt))
-               kfree(m);
+       fib6_metrics_release(f6i);
 
        kfree(f6i);
 }
@@ -887,6 +896,7 @@ static void fib6_drop_pcpu_from(struct fib6_info *f6i,
 
                        from = rcu_dereference_protected(pcpu_rt->from,
                                             lockdep_is_held(&table->tb6_lock));
+                       fib6_metrics_release(from);
                        rcu_assign_pointer(pcpu_rt->from, NULL);
                        fib6_info_release(from);
                }