diff options
author | Sabrina Dubroca <sd@queasysnail.net> | 2018-07-30 16:23:10 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-07-30 09:45:57 -0700 |
commit | df18b50448fab1dff093731dfd0e25e77e1afcd1 (patch) | |
tree | 0afb2a9351d3d591df033c400255db700d581200 /net/ipv6/ip6_fib.c | |
parent | 822fb18a82abaf4ee7058793d95d340f5dab7bfc (diff) |
net/ipv6: fix metrics leak
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>
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index d212738e9d10..211a2d437b56 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -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); } |