CVE-2026-46116: xfrm: defensively unhash xfrm_state lists in __xfrm_state_delete
In the Linux kernel, the following vulnerability has been resolved:
xfrm: defensively unhash xfrmstate lists in xfrmstatedelete
KASAN reproduces a slab-use-after-free in xfrmstatedelete()'s hlistdelrcu calls under syzkaller load on linux-6.12.y stable (reproduced on 6.12.47, also reachable via the same code path on torvalds/master and on the ipsec tree). Nine unique signatures cluster in the xfrmstate lifecycle, the load-bearing one being:
BUG: KASAN: slab-use-after-free in hlistdel include/linux/list.h:990 [inline] BUG: KASAN: slab-use-after-free in hlistdelrcu include/linux/rculist.h:516 [inline] BUG: KASAN: slab-use-after-free in xfrmstatedelete net/xfrm/xfrmstate.c Write of size 8 at addr ffff8881198bcb70 by task kworker/u8:9/435
Workqueue: netns cleanupnet Call Trace: hlistdel / hlistdelrcu xfrmstatedelete xfrmstatedelete xfrmstateflush xfrmstatefini opsexitlist cleanupnet
The other observed signatures hit the same slab object from xfrmstatelookup, xfrmallocspi, xfrmstateinsert and an OOB write variant of xfrmstatedelete, all on the byseq/byspi hash chains.
xfrmstatedelete() guards its byseq and byspi unhashes with value-based predicates:
if (x->km.seq) hlistdelrcu(&x->byseq); if (x->id.spi) hlistdelrcu(&x->byspi);
while everywhere else in the file (e.g. statecache, statecacheinput) the safer hlistunhashed() check is used. xfrmallocspi() sets x->id.spi = newspi inside xfrmstatelock and then immediately inserts into byspi, but a path that observes x->id.spi != 0 outside of xfrmstatelock can still skip-or-hit the byspi unhash inconsistently with whether x is actually on the list. The same holds for x->km.seq versus byseq, and the bydst/bysrc unhashes have no predicate at all, so a second xfrmstatedelete() on the same object writes through LISTPOISON pprev.
The defensive change here:
- Use hlistdelinitrcu() instead of hlistdelrcu() on bydst, bysrc, byseq and byspi so a second deletion is a no-op rather than a write through LISTPOISON pprev. The byseq/byspi nodes are already initialised in xfrmstatealloc(). - Test hlistunhashed() rather than the value predicate for byseq/byspi, so the unhash decision tracks list state rather than mutable scalar fields.
Empirical verification: applied this patch on top of v6.12.47, rebuilt, and re-ran the same syzkaller harness for 1h16m on a previously-crashy configuration that produced ~100 hits each of slab-use-after-free Read in xfrmallocspi / Read in xfrmstatelookup / Write in xfrmstatedelete. After the patch, 7.1M execs across 32 VMs at ~1550 exec/sec produced zero xfrmstate UAF/OOB hits. /proc/slabinfo confirms the xfrmstate slab is actively allocated and freed during the run (~143 KiB resident), so the fuzzer is still exercising those code paths -- they just no longer crash.
Reproduction:
- Linux 6.12.47 x8664 + KASANGENERIC + KASANINLINE + KCOV - syzkaller @ 746545b8b1e4c3a128db8652b340d3df90ce61db - 32 QEMU/KVM VMs x 2 vCPU on AWS c5.metal bare metal - 9 unique signatures collected in ~9h, all within xfrmstate lifecycle
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade to a fixed release to a version that resolves this vulnerability.
Fixed in 6.6.141.1-1 - Upgrade
Upgrade
Linux kernelto a version that resolves this vulnerability.Patch syzkaller @ 746545b8b1e4c3a128db8652b340d3df90ce61db - Configuration
Apply the defensive change in xfrm_state: use hlist_del_init_rcu() rather than hlist_del_rcu() on bydst (as described: “Use hlist_del_init_rcu() instead of hlist_del_rcu() on bydst”).
Linux kernel (xfrm: xfrm_state) unhash_byseq_byspi_using = hlist_del_init_rcu() instead of hlist_del_rcu() on bydst - Configuration
In __xfrm_state_delete(), guard the byseq/byspi unhash decision using hlist_unhashed() (instead of value-based predicates) so that a second deletion becomes a no-op rather than touching LIST_POISON pprev.
Linux kernel (xfrm: xfrm_state) unhash_membership_check = use hlist_unhashed() rather than value predicate for list membership - Configuration
Update __xfrm_state_delete() so the unhash decision for byseq/byspi tracks list state (not mutable scalar fields such as x->id.spi / x->km.seq), preventing OOB or UAF on repeated deletion under netns cleanup paths (e.g., cleanup_net → workqueue: netns cleanup_net).
Linux kernel (xfrm: __xfrm_state_delete) unhash_logic_targets = track list state rather than bysrc/byseq/byspi scalar fields
Event History
Frequently Asked Questions
What level of access does an attacker need?
The CVSS vector indicates local access with low privileges is required. No user interaction is required.
What is the likely security impact?
The vulnerability is classified as a use-after-free with high confidentiality impact and low availability impact. The supplied CVSS vector does not indicate an integrity impact.
Which environments were directly observed to reach the vulnerable code path?
The issue was reproduced under syzkaller load on Linux 6.12.47 in the 6.12.y stable series. The same code path was also reported as reachable on torvalds/master and the IPsec tree.
Which products are identified in the supplied data?
The listed software includes the Linux kernel and Microsoft azl3 kernel 6.6.139.1-1.