CVE-2026-98369: xfrm: add missing rcu_read_lock(), skb_dst_force() and dev_hold() for xfrm_trans_reinject()
In the Linux kernel, the following vulnerability has been resolved:
xfrm: add missing rcureadlock(), skbdstforce() and devhold() for xfrmtransreinject()
syzbot reported a suspicious RCU usage warning in ip6pktdrop():
WARNING: suspicious RCU usage in ip6pktdrop include/net/addrconf.h:389 suspicious rcudereferencecheck() usage!
Call Trace: in6devgetsafely include/net/addrconf.h:389 [inline] ip6pktdrop+0x596/0x610 net/ipv6/route.c:4620 ip6pktdiscard+0x1c/0x30 net/ipv6/route.c:4651 xfrmtransreinject+0x324/0x630 net/xfrm/xfrminput.c:806 processonework kernel/workqueue.c:3322 [inline] processscheduledworks+0xa8e/0x14e0 kernel/workqueue.c:3405 workerthread+0xa47/0xfb0 kernel/workqueue.c:3486
When commit 4f4920669d21 ("xfrm: Reinject transport-mode packets through workqueue") converted xfrmtransreinject from a tasklet to a workqueue, the reinjection loop ceased running in softirq context. Workqueue workers run in process context where localbhdisable() does not enter an RCU read-side critical section under CONFIGPREEMPTRCU.
Because finish callbacks (such as ip6rcvfinish) expect to run under an RCU read lock (performing route lookups, l3mdev lookups, and accessing RCU-protected data structures), invoking them in workqueue context without rcureadlock() triggers RCU lockdep warnings.
Furthermore, packets queued to the workqueue via xfrmtransqueuenet() may carry non-refcounted (noref) dst entries (e.g. from iprouteinputnoref). Additionally, on netdevice unregistration, dstdevput() replaces dst->dev with blackholenetdev, so dst entries do not keep skb->dev alive while queued in the workqueue.
Fix these issues by: 1. Calling skbdstforce(skb) in xfrmtransqueuenet() while still in the caller's RCU section to ensure dst is reference-counted before queuing. 2. Holding a reference on skb->dev via devhold()/devput() across workqueue deferral so skb->dev remains valid during finish() callback processing. 3. Acquiring rcureadlock() around the finish callback invocation loop in xfrmtransreinject().
Affected Software
Event History
Frequently Asked Questions
Is CONFIG_PREEMPT_RCU relevant to this issue?
Yes. The reported condition occurs because workqueue workers run in process context, where local_bh_disable() does not enter an RCU read-side critical section under CONFIG_PREEMPT_RCU.
What code path is involved in the reported warning?
The warning was reported during transport-mode packet reinjection through xfrm_trans_reinject(), with the call trace reaching ip6_pkt_drop() and ip6_pkt_discard(). The affected reinjection loop was moved from tasklet context to a workqueue.