| From foo@baz Thu Sep 14 23:20:08 PDT 2017 |
| From: Sabrina Dubroca <sd@queasysnail.net> |
| Date: Fri, 8 Sep 2017 10:26:19 +0200 |
| Subject: ipv6: fix memory leak with multiple tables during netns destruction |
| |
| From: Sabrina Dubroca <sd@queasysnail.net> |
| |
| |
| [ Upstream commit ba1cc08d9488c94cb8d94f545305688b72a2a300 ] |
| |
| fib6_net_exit only frees the main and local tables. If another table was |
| created with fib6_alloc_table, we leak it when the netns is destroyed. |
| |
| Fix this in the same way ip_fib_net_exit cleans up tables, by walking |
| through the whole hashtable of fib6_table's. We can get rid of the |
| special cases for local and main, since they're also part of the |
| hashtable. |
| |
| Reproducer: |
| ip netns add x |
| ip -net x -6 rule add from 6003:1::/64 table 100 |
| ip netns del x |
| |
| Reported-by: Jianlin Shi <jishi@redhat.com> |
| Fixes: 58f09b78b730 ("[NETNS][IPV6] ip6_fib - make it per network namespace") |
| Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv6/ip6_fib.c | 25 +++++++++++++++++++------ |
| 1 file changed, 19 insertions(+), 6 deletions(-) |
| |
| --- a/net/ipv6/ip6_fib.c |
| +++ b/net/ipv6/ip6_fib.c |
| @@ -201,6 +201,12 @@ static void rt6_release(struct rt6_info |
| } |
| } |
| |
| +static void fib6_free_table(struct fib6_table *table) |
| +{ |
| + inetpeer_invalidate_tree(&table->tb6_peers); |
| + kfree(table); |
| +} |
| + |
| static void fib6_link_table(struct net *net, struct fib6_table *tb) |
| { |
| unsigned int h; |
| @@ -1893,15 +1899,22 @@ out_timer: |
| |
| static void fib6_net_exit(struct net *net) |
| { |
| + unsigned int i; |
| + |
| rt6_ifdown(net, NULL); |
| del_timer_sync(&net->ipv6.ip6_fib_timer); |
| |
| -#ifdef CONFIG_IPV6_MULTIPLE_TABLES |
| - inetpeer_invalidate_tree(&net->ipv6.fib6_local_tbl->tb6_peers); |
| - kfree(net->ipv6.fib6_local_tbl); |
| -#endif |
| - inetpeer_invalidate_tree(&net->ipv6.fib6_main_tbl->tb6_peers); |
| - kfree(net->ipv6.fib6_main_tbl); |
| + for (i = 0; i < FIB_TABLE_HASHSZ; i++) { |
| + struct hlist_head *head = &net->ipv6.fib_table_hash[i]; |
| + struct hlist_node *tmp; |
| + struct fib6_table *tb; |
| + |
| + hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) { |
| + hlist_del(&tb->tb6_hlist); |
| + fib6_free_table(tb); |
| + } |
| + } |
| + |
| kfree(net->ipv6.fib_table_hash); |
| kfree(net->ipv6.rt6_stats); |
| } |