$RodHat_
MOTD

CVE-2026-46116: use-after-free in xfrm, distros are just now patching it

Published by

CVE-2026-46116: use-after-free in xfrm, distros are just now patching it
Photo: AI-generated — no human photographer / RodHat AI Cover

CVE-2026-46116 is a slab-use-after-free in __xfrm_state_delete(), the function that tears down IPsec Security Associations in the kernel’s xfrm subsystem. Upstream fixed it in May. Patched versions landed across all the LTS branches: 5.15.210, 6.1.176, 6.6.140, 6.12.88, 6.18.30, 7.0.7. Ubuntu is shipping those fixes this week. SUSE pushed patches on October 9. Older Ubuntu LTS releases (20.04, 18.04, 16.04) are still working through the queue.

CVSS 7.8. Local privilege escalation. No remote component. You need local access and the ability to touch network namespaces or trigger IPsec state churn. On most Linux distros, that bar is lower than you’d like.

What xfrm is and why it’s in everything

xfrm is the kernel’s transform framework. It’s the plumbing underneath IPsec: every Security Association (SA) created for an IKE tunnel, every in-kernel encrypt/decrypt operation on ESP or AH packets, every policy rule mapping a flow to a transform runs through it. WireGuard punted on xfrm entirely and does its own crypto handling in userspace-adjacent kernel code, but IPsec is not going away. Enterprise VPNs, site-to-site tunnels, and anything talking to Cisco or Juniper gear is almost certainly running xfrm under the hood, often without the sysadmin realizing it.

There’s a lot of state involved. xfrm keeps hash tables of xfrm_state objects keyed by SPI, source address, destination address, and protocol. When a tunnel tears down or a namespace is cleaned up, those objects need to be removed from the hash tables and freed. That teardown is where this bug lives.

The bug

__xfrm_state_delete() is responsible for removing an xfrm_state from all the hash tables that reference it. The problem is that it was checking whether a state was already off a list by testing a value predicate (“is this field set to X?”) rather than by testing actual list membership. Under syzkaller load, specifically during namespace cleanup racing with xfrm_state expiry, you could end up with a state that had already been removed from a list but whose predicate said it was still there.

The result: hlist_del_rcu() runs on a node that’s already been removed. At that point you’re writing through list pointers into freed slab memory. Classic use-after-free, with all the attendant possibilities for corrupting kernel heap layout until something useful is at the right offset.

Red Hat’s syzkaller confirmation run on v6.12.47 (post-patch) ran 7.1 million executions across 32 VMs for over an hour with zero UAF hits. Pre-patch, the harness was reliably trigging it. The bug reproduces under load; it’s not theoretical.

The fix is instructive

The patch switches from hlist_del_rcu() to hlist_del_init_rcu() and adds explicit “am I on a list?” checks before any removal.

The distinction matters. hlist_del_rcu() removes the node from the list but leaves its own next and pprev pointers pointing wherever they were. A double-deletion runs those stale pointers through hlist_del_rcu() again and writes garbage into whatever slab memory they now point at. hlist_del_init_rcu() does the same removal, then zeroes out next and pprev. A double-deletion now dereferences null and panics the kernel. That’s a hard crash, which is annoying, but it’s not a privilege escalation.

This pattern, using _init_rcu variants that null out their own pointers after removal, is the standard defensive approach for any list node that might be encountered in multiple teardown paths. The bug is that xfrm wasn’t consistently doing it. The fix is adding that consistency, plus guard checks so that __xfrm_state_delete refuses to operate on a state that’s already been removed.

What you should do

Run uname -r and check whether your distro has shipped a patched kernel:

  • Ubuntu 26.04 LTS: linux 7.0.0-28.28 or later
  • Ubuntu 24.04 LTS: linux 6.8.0-136.136 or later
  • SUSE Linux Enterprise 16: kernel 6.12.0-160000.28.1 (October 9, 2026)
  • Upstream LTS branches: 5.15.210, 6.1.176, 6.6.140, 6.12.88, 7.0.7

If you’re on Ubuntu 20.04, 18.04, or 16.04: check your distro’s errata, patches are in progress. If you’re running bare-metal IPsec and can’t patch immediately, restricting unprivileged user namespace creation (kernel.unprivileged_userns_clone=0 or the equivalent in your distro’s sysctl.d) closes the most accessible attack path. It’s a blunt instrument, but it raises the bar while you wait for the patch.

The prctl hardening tip covers the per-process privilege controls that matter for reducing attack surface in this class of bug. The mseal work is a different angle on the same underlying problem: kernel memory layouts that don’t have enough invariants enforced at allocation time.

Five months

From upstream fix (May 28) to distro patches (October) is a long time for a CVSS 7.8 local privilege escalation. Ubuntu’s priority classification of this as “Medium” rather than “High” is probably why it took this long to land in the LTS queues.

Rod’s position on CVSS scoring is already on record. The number is often wrong in both directions, and a local priv esc in xfrm affects any Linux box running IPsec with untrusted local users. Most servers. Most containers. Most anything with network namespaces enabled.

Patch your kernels.

Sources

  1. CVE-2026-46116: kernel: xfrm: defensively unhash xfrm_state lists in __xfrm_state_delete (Red Hat Bugzilla)
  2. CVE-2026-46116 Ubuntu Security Notice (Ubuntu Security)