Re: net: rtnetlink: rtnl_link_unregister() vs in-flight RTM_GETLINK dump readers -- v7.3 lacks the synchronize_rcu() fix (Fixes: 6b57ff21a310 family); A/B reproduction on stock 7.3

From: Eric Dumazet

Date: Tue Nov 03 2026 - 10:57:52 EST


Le mar. 3 nov. 2026 à 12:57, 徐晗翔 <xuhx@xxxxxxxxxxx> a écrit :
>
> On v7.3, rtnl_link_unregister() removes the rtnl_link_ops from the
> global list and immediately destroys the embedded srcu_struct and
> frees the ops memory -- a concurrent RTM_GETLINK dump that already
> found the ops (rtnl_link_ops_get(), rtnetlink.c:599 hand-off inside
> an RCU read critical section at :595) but has not yet taken
> ops->srcu will, after the free, execute __srcu_read_lock() /
> __srcu_read_unlock() on freed memory: two reads of the destroyed
> object (ssp->srcu_ctrp, ssp->sda) whose subsequent percpu counter
> writes land wherever the freed percpu pointers point -- for a module
> rtnl_link_ops, the ops (and its srcu) live in module memory, so
> module_memfree() is the freeing side. Upstream already fixed this
> with a synchronize_rcu() in rtnl_link_unregister() (the patch with
> Fixes: 6b57ff21a310; its own comment: "Wait for rtnl_link_ops_get()
> readers that found ops but have not yet taken ops->srcu"). v7.3
> does not contain it. I verified both sides on a stock 7.3 KASAN
> kernel: parking a real dump reader (driven by an ordinary
> RTM_GETLINK + IFLA_LINKINFO dump request) exactly at the :599
> hand-off and unregistering+freeing underneath produces three KASAN
> slab-use-after-free reports (__srcu_read_lock+0xe, +0x25,
> __srcu_read_unlock+0x1a; bad address = ops+0x10 = ops->srcu); and
> rebuilding the SAME kernel with ONLY the upstream one-line fix
> flips the outcome to zero reports, with the kfree demonstrably
> delayed until the parked reader left its RCU section. Natural
> hammering did not hit (the hand-off window is ~100 ns against a
> ~4 ms unregister -- it needs a preempted/preemptible-RCU reader),
> which matches the fix's own reasoning; the controlled run models
> exactly that preemption. The purpose of this mail is to support
> the fix's propagation: trees still carrying the unfixed
> rtnl_link_unregister() keep a cross-CPU UAF (write) plus potential
> freed-module execution reachable by unprivileged netlink dumps
> racing a module unload.
>
>
> The detailed description:
>
> * The gap (v7.3, net/core/rtnetlink.c)
> rtnl_dump_ifinfo() runs lockless for dumps
> (RTNL_FLAG_DUMP_UNLOCKED), takes rcu_read_lock() at :595, calls
> linkinfo_to_kind_ops() -> rtnl_link_ops_get() which returns the
> ops pointer at :599, and only then takes ops->srcu
> (srcu_read_lock) to call ops->dumpit. rtnl_link_unregister()
> holds rtnl_mutex, removes the ops from the list, calls
> cleanup_srcu_struct(&ops->srcu) and returns to the module, whose
> exit then frees the module memory. Nothing between the :599
> hand-off and the srcu_read_lock is waited for on the unregister
> side in 7.3 -- hence the fix's synchronize_rcu().
>
> * Evidence (stock 7.3 KASAN, QEMU; A/B on the one-line fix;
> reproducers attached)
> rtnlrace.c registers a kzalloc'd rtnl_link_ops (kind "rtnlrace"),
> parks a dump reader by kprobe at the exact hand-off (return
> address rtnl_dump_ifinfo+0x374, ENDBR-adjusted), and unregisters
> + kfrees from another thread (modeling module_exit ->
> module_memfree). nlr (userspace) sends the IFLA_LINKINFO dump
> request that drives the reader path.
> Defect kernel (stock 7.3):
> park t=127041 (1004 ms) -> unregister+kfree at t=127249 (park
> +208 ms, reader still parked 792 ms) -> park ends t=128045:
> BUG: KASAN: slab-use-after-free in __srcu_read_lock+0xe
> BUG: KASAN: slab-use-after-free in __srcu_read_lock+0x25
> BUG: KASAN: slab-use-after-free in __srcu_read_unlock+0x1a
> read stack: __srcu_read_lock <- rtnl_dump_ifinfo+0x374 <-
> rtnl_dumpit <- netlink_dump <- rtnetlink_rcv_msg (task nlr)
> bad address = object+0x10 == ops->srcu (matches the
> disassembly lea 0x10(%rbx))
> alloc: insmod kzalloc; free: unregister thread kfree
> Fixed kernel (same sources, ONLY the upstream synchronize_rcu()
> added to rtnl_link_unregister()):
> park t=191305 -> park ENDS t=192306 -> unregister+kfree
> t=192307 -- the order literally inverts -- zero KASAN reports,
> dump completes with zero errors. Same module, same parking.
> Natural runs (three samplings, up to ~267k dumps/reader plus
> 6-CPU preemption spinners): zero hits; the window arithmetic
> (~100 ns hand-off vs ~4 ms unregister) explains it, and the
> controlled park models precisely the preemption the upstream
> fix's comment describes.
>

What is the point of this email ? This looks like LLM slope to me.

I think we have far more serious bugs to fix.

3b70edea393d was tagged stable+noautosel on purpose: the race needs
a module unload (CAP_SYS_MODULE in init_user_ns) to line up with a
~100ns window, which your report also shows you could not hit
without a kprobe. It is not reachable by an unprivileged user alone.