Re: [PATCH net v2 1/1] ipv6: ip6mr: fix mr_table leak from MRT6_TABLE
From: Ido Schimmel <idosch@nvidia.com>
Date: 2026-09-06 18:19:28
Also in:
lkml, stable
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
On Sat, Sep 05, 2026 at 04:30:07PM +0000, Zihan Xi wrote:
MRT6_TABLE is supposed to select a multicast routing table id, but ip6_mroute_setsockopt() currently calls ip6mr_new_table() for every unseen id. The new mr_table is linked into mr6_tables and is only destroyed when the net namespace goes away. A raw ICMPv6 socket with CAP_NET_ADMIN can therefore loop MRT6_TABLE(fresh id) without MRT6_INIT, close the socket, and still leave the tables allocated. Repeating this grows unreclaimable slab until the machine OOMs.
Isn't the established way to deal with this sort of issue to simply
account for the memory and assume that the admin put some kind of a
memory limit on the container?
See for example commit 6126891c6d4f ("memcg: enable accounting for IP
address and routing-related objects") and commit 5d26cff5bdbe ("net:
account alternate interface name memory").
This diff [1] accounts for the memory of both the multicast tables
and the multicast routes.
With your C reproducer and this diff I get:
# echo +memory > /sys/fs/cgroup/cgroup.subtree_control
# mkdir /sys/fs/cgroup/mrtest
# echo 64M > /sys/fs/cgroup/mrtest/memory.max
# echo $$ > /sys/fs/cgroup/mrtest/cgroup.procs
# unshare -Urn ./mrt_poc
allocated=1 current_table=1 maxrss_kb=1788
allocated=10001 current_table=10001 maxrss_kb=1788
Killed bash
# dmesg | tail -1
[ 63.360047] Memory cgroup out of memory: Killed process 297 (mrt_poc) total-vm:2424kB, anon-rss:92kB, file-rss:1448kB, shmem-rss:0kB, UID:0 pgtables:44kB oom_score_adj:0
[1]diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index e5f2b1c6150d..b9c544d48c45 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c@@ -3376,7 +3376,8 @@ int __init ip_mr_init(void) { int err; - mrt_cachep = KMEM_CACHE(mfc_cache, SLAB_HWCACHE_ALIGN | SLAB_PANIC); + mrt_cachep = KMEM_CACHE(mfc_cache, + SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT); err = register_pernet_subsys(&ipmr_net_ops); if (err)
diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c
index 867b24beded1..a0ec6d19a237 100644
--- a/net/ipv4/ipmr_base.c
+++ b/net/ipv4/ipmr_base.c@@ -52,7 +52,7 @@ mr_table_alloc(struct net *net, u32 id, struct mr_table *mrt; int err; - mrt = kzalloc_obj(*mrt); + mrt = kzalloc_obj(*mrt, GFP_KERNEL_ACCOUNT); if (!mrt) return ERR_PTR(-ENOMEM); mrt->id = id;
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 3f2ed9b77deb..9d8116b5edb1 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c@@ -1427,7 +1427,7 @@ int __init ip6_mr_init(void) { int err; - mrt_cachep = KMEM_CACHE(mfc6_cache, SLAB_HWCACHE_ALIGN); + mrt_cachep = KMEM_CACHE(mfc6_cache, SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT); if (!mrt_cachep) return -ENOMEM;