aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2019-05-15 22:39:52 -0400
committerDavid S. Miller <davem@davemloft.net>2019-05-16 15:21:00 -0400
commit61fb0d01680771f72cc9d39783fb2c122aaad51e (patch)
tree6b09b1806f97585a5259d909d0bfa47c93472d52 /net/ipv6
parent185ce5c38ea76f29b6bd9c7c8c7a5e5408834920 (diff)
ipv6: prevent possible fib6 leaks
At ipv6 route dismantle, fib6_drop_pcpu_from() is responsible for finding all percpu routes and set their ->from pointer to NULL, so that fib6_ref can reach its expected value (1). The problem right now is that other cpus can still catch the route being deleted, since there is no rcu grace period between the route deletion and call to fib6_drop_pcpu_from() This can leak the fib6 and associated resources, since no notifier will take care of removing the last reference(s). I decided to add another boolean (fib6_destroying) instead of reusing/renaming exception_bucket_flushed to ease stable backports, and properly document the memory barriers used to implement this fix. This patch has been co-developped with Wei Wang. Fixes: 93531c674315 ("net/ipv6: separate handling of FIB entries from dst based routes") Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: syzbot <syzkaller@googlegroups.com> Cc: Wei Wang <weiwan@google.com> Cc: David Ahern <dsahern@gmail.com> Cc: Martin Lau <kafai@fb.com> Acked-by: Wei Wang <weiwan@google.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Reviewed-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_fib.c12
-rw-r--r--net/ipv6/route.c7
2 files changed, 16 insertions, 3 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 08e0390e001c..008421b550c6 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -904,6 +904,12 @@ static void fib6_drop_pcpu_from(struct fib6_info *f6i,
904{ 904{
905 int cpu; 905 int cpu;
906 906
907 /* Make sure rt6_make_pcpu_route() wont add other percpu routes
908 * while we are cleaning them here.
909 */
910 f6i->fib6_destroying = 1;
911 mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */
912
907 /* release the reference to this fib entry from 913 /* release the reference to this fib entry from
908 * all of its cached pcpu routes 914 * all of its cached pcpu routes
909 */ 915 */
@@ -927,6 +933,9 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
927{ 933{
928 struct fib6_table *table = rt->fib6_table; 934 struct fib6_table *table = rt->fib6_table;
929 935
936 if (rt->rt6i_pcpu)
937 fib6_drop_pcpu_from(rt, table);
938
930 if (refcount_read(&rt->fib6_ref) != 1) { 939 if (refcount_read(&rt->fib6_ref) != 1) {
931 /* This route is used as dummy address holder in some split 940 /* This route is used as dummy address holder in some split
932 * nodes. It is not leaked, but it still holds other resources, 941 * nodes. It is not leaked, but it still holds other resources,
@@ -948,9 +957,6 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
948 fn = rcu_dereference_protected(fn->parent, 957 fn = rcu_dereference_protected(fn->parent,
949 lockdep_is_held(&table->tb6_lock)); 958 lockdep_is_held(&table->tb6_lock));
950 } 959 }
951
952 if (rt->rt6i_pcpu)
953 fib6_drop_pcpu_from(rt, table);
954 } 960 }
955} 961}
956 962
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 23a20d62daac..27c0cc5d9d30 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1295,6 +1295,13 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net,
1295 prev = cmpxchg(p, NULL, pcpu_rt); 1295 prev = cmpxchg(p, NULL, pcpu_rt);
1296 BUG_ON(prev); 1296 BUG_ON(prev);
1297 1297
1298 if (res->f6i->fib6_destroying) {
1299 struct fib6_info *from;
1300
1301 from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
1302 fib6_info_release(from);
1303 }
1304
1298 return pcpu_rt; 1305 return pcpu_rt;
1299} 1306}
1300 1307