rtnl_link_ops_get() finds ops on link_ops under rcu_read_lock() and only then takes ops->srcu. rtnl_link_unregister() unlinks ops, then calls synchronize_srcu() and cleanup_srcu_struct() without first waiting for RCU readers. A reader that found ops before list_del_rcu() and has not yet taken ops->srcu is not waited for. Such a reader then locks a cleaned-up srcu_struct and keeps using ops after the module that owns it is gone. rtnl_newlink() and rtnl_dump_ifinfo() both look up ops without RTNL, the latter for any user that dumps links filtered by kind. With delays added between the lookup and srcu_read_lock() in rtnl_link_ops_get(), and before rtnl_link_ops_put() in rtnl_dump_ifinfo(), running "ip link show type dummy" alongside "rmmod dummy" on a KASAN kernel gives: BUG: KASAN: vmalloc-out-of-bounds in __srcu_read_unlock+0x57/0x70 Read of size 8 at addr ffffffffc02ff318 by task ip/147 Call Trace: __srcu_read_unlock+0x57/0x70 rtnl_dump_ifinfo.cold+0x30/0x3b rtnl_dumpit+0x7a/0x1b0 netlink_dump+0x486/0x11b0 __netlink_dump_start+0x661/0x910 rtnetlink_rcv_msg+0x611/0x9e0 netlink_rcv_skb+0x126/0x380 netlink_unicast+0x605/0x830 netlink_sendmsg+0x6d0/0xbb0 __sys_sendto+0x335/0x3a0 Call synchronize_rcu() before synchronize_srcu(), as rtnl_af_unregister() already does. This should be enough because rtnl_link_ops_get() is the only RCU walker of link_ops, and it takes ops->srcu before rcu_read_unlock(), so every reader that found ops is covered by synchronize_srcu() once the RCU grace period ends. The extra grace period only costs time at module unload. Readers and the writer were serialized by RTNL until link_ops got its own mutex, which is why the missing RCU wait did not matter before. Fixes: 6b57ff21a310 ("rtnetlink: Protect link_ops by mutex.") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Andrea Parri --- net/core/rtnetlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index be9d1625bac31..abcd2c4ea2178 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -707,6 +707,7 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops) list_del_rcu(&ops->list); mutex_unlock(&link_ops_mutex); + synchronize_rcu(); synchronize_srcu(&ops->srcu); cleanup_srcu_struct(&ops->srcu); base-commit: 54518e0e827f4ca9229ae657022c60bf60f5c1bf -- 2.53.0