Re: [PATCH v1 net] ipmr: Free mr_table after RCU grace period.
From: Kuniyuki Iwashima
Date: Wed May 06 2026 - 02:20:46 EST
On Tue, May 5, 2026 at 11:00 PM Lai, Yi <yi1.lai@xxxxxxxxx> wrote:
>
> On Thu, Apr 23, 2026 at 05:34:54AM +0000, Kuniyuki Iwashima wrote:
> > With CONFIG_IP_MROUTE_MULTIPLE_TABLES=n, ipmr_fib_lookup()
> > does not check if net->ipv4.mrt is NULL.
> >
> > Since default_device_exit_batch() is called after ->exit_rtnl(),
> > a device could receive IGMP packets and access net->ipv4.mrt
> > during/after ipmr_rules_exit_rtnl().
> >
> > If ipmr_rules_exit_rtnl() had already cleared it and freed the
> > memory, the access would trigger null-ptr-deref or use-after-free.
> >
> > Let's fix it by using RCU helper and free mrt after RCU grace
> > period.
> >
> > In addition, check_net(net) is added to mroute_clean_tables()
> > and ipmr_cache_unresolved() to synchronise via mfc_unres_lock.
> > This prevents ipmr_cache_unresolved() from putting skb into
> > c->_c.mfc_un.unres.unresolved after mroute_clean_tables()
> > purges it.
> >
> > For the same reason, timer_shutdown_sync() is moved after
> > mroute_clean_tables().
> >
> > Since rhltable_destroy() holds mutex internally, rcu_work is
> > used, and it is placed as the first member because rcu_head
> > must be placed within <4K offset. mr_table is alraedy 3864
> > bytes without rcu_work.
> >
> > Note that IP6MR is not yet converted to ->exit_rtnl(), so this
> > change is not needed for now but will be.
> >
> > Fixes: b22b01867406 ("ipmr: Convert ipmr_net_exit_batch() to ->exit_rtnl().")
> > Signed-off-by: Kuniyuki Iwashima <kuniyu@xxxxxxxxxx>
>
> Hi Kuniyuki Iwashima,
>
> Greetings!
>
> I used Syzkaller and found that there is WARNING: suspicious RCU usage in reg_vif_xmit in linux-next next-20260505.
>
> After bisection and the first bad commit is:
> "
> b3b6babf4751 ipmr: Free mr_table after RCU grace period
> "
>
> All detailed into can be found at:
> https://github.com/laifryiee/syzkaller_logs/tree/main/260506_091248_reg_vif_xmit
> Syzkaller repro code:
> https://github.com/laifryiee/syzkaller_logs/tree/main/260506_091248_reg_vif_xmit/repro.c
> Syzkaller repro syscall steps:
> https://github.com/laifryiee/syzkaller_logs/tree/main/260506_091248_reg_vif_xmit/repro.prog
> Syzkaller report:
> https://github.com/laifryiee/syzkaller_logs/tree/main/260506_091248_reg_vif_xmit/repro.report
> Kconfig(make olddefconfig):
> https://github.com/laifryiee/syzkaller_logs/tree/main/260506_091248_reg_vif_xmit/kconfig_origin
> Bisect info:
> https://github.com/laifryiee/syzkaller_logs/tree/main/260506_091248_reg_vif_xmit/bisect_info.log
> bzImage:
> https://github.com/laifryiee/syzkaller_logs/raw/refs/heads/main/260506_091248_reg_vif_xmit/bzImage_next-20260505
> Issue dmesg:
> https://github.com/laifryiee/syzkaller_logs/blob/main/260506_091248_reg_vif_xmit/next-20260505_dmesg.log
>
> "
> [ 18.611146] =============================
> [ 18.611406] WARNING: suspicious RCU usage
> [ 18.611657] 7.1.0-rc2-next-20260505-next-2026050 #1 Not tainted
> [ 18.612022] -----------------------------
> [ 18.612289] net/ipv4/ipmr.c:329 suspicious rcu_dereference_check() usage!
> [ 18.612755]
> [ 18.612755] other info that might help us debug this:
> [ 18.612755]
> [ 18.613314]
> [ 18.613314] rcu_scheduler_active = 2, debug_locks = 1
> [ 18.613758] 2 locks held by repro/725:
> [ 18.614195] #0: ffffffff87896440 (rcu_read_lock_bh){....}-{1:3}, at: __dev_queue_xmit+0x239/0x4140
> [ 18.614860] #1: ff1100000df5b918 (_xmit_PIMREG#2){+...}-{3:3}, at: __dev_queue_xmit+0x1d5d/0x4140
> [ 18.615505]
> [ 18.615505] stack backtrace:
> [ 18.615814] CPU: 0 UID: 0 PID: 725 Comm: repro Not tainted 7.1.0-rc2-next-20260505-next-2026050 #1 PREEMPT(lazy)
> [ 18.615826] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu4
> [ 18.615831] Call Trace:
> [ 18.615834] <TASK>
> [ 18.615838] dump_stack_lvl+0x121/0x150
> [ 18.615853] dump_stack+0x19/0x20
> [ 18.615864] lockdep_rcu_suspicious+0x15b/0x1f0
> [ 18.615882] reg_vif_xmit+0x2ee/0x3c0
Thanks for the report.
I'll just move up rcu_read_lock() in reg_vif_xmit().
ipmr_fib_lookup() for CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
calls rcu_read_lock() at the same timing anyway.