3 분 소요

Summary

I found and fixed a Linux kernel XFRM policy use-after-free now tracked as CVE-2026-53239. The upstream fix is commit 7f2d76c9c032, titled xfrm: policy: fix use-after-free on inexact bin in xfrm_policy_bysel_ctx().

The bug was in net/xfrm/xfrm_policy.c. xfrm_policy_bysel_ctx() could keep a raw pointer to an inexact policy bin after dropping xfrm_policy_lock; a concurrent XFRM hash rebuild could free that bin before the original path later pruned it.

The public CVE record lists the kernel.org CNA CVSS v3.1 vector as CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H.

Root cause

The vulnerable path was a race between policy deletion and policy hash rebuilding.

On the deletion side, xfrm_policy_bysel_ctx() looked up an inexact-bin pointer while holding xfrm_policy_lock, unlinked the selected policy, dropped the lock, killed the policy, and then called xfrm_policy_inexact_prune_bin() with the saved bin pointer.

On the rebuild side, xfrm_hash_rebuild() could take the same lock and flush inexact bins. That flush path could release the bin with kfree_rcu(). If this happened during the unlocked window, the deletion path later used a stale bin pointer.

In short:

CPU0: XFRM_MSG_DELPOLICY              CPU1: XFRM_MSG_NEWSPDINFO
--------------------------------      --------------------------------
xfrm_policy_bysel_ctx()
  lock xfrm_policy_lock
  bin = xfrm_policy_inexact_lookup()
  __xfrm_policy_unlink(pol)
  unlock xfrm_policy_lock
  xfrm_policy_kill(ret)
                                      xfrm_hash_rebuild()
                                        lock xfrm_policy_lock
                                        __xfrm_policy_inexact_flush()
                                          kfree_rcu(bin)
                                        unlock xfrm_policy_lock
  xfrm_policy_inexact_prune_bin(bin)

The last line is the stale access.

Evidence

My local evidence was a KASAN-confirmed lifetime violation on a debug kernel, with timing adjusted to force the RCU grace period to complete before the stale access. The report showed a slab use-after-free in xfrm_policy_bysel_ctx(), reached from xfrm_get_policy() and xfrm_user_rcv_msg().

That caveat matters: the bug shape is real, but the local reproducer used debug timing to make the race observable. I do not claim a public exploit from this evidence.

Fix

The fix keeps the bin prune inside the original critical section. Instead of saving the bin pointer, dropping the lock, and then re-acquiring the lock through xfrm_policy_inexact_prune_bin(), the patch calls __xfrm_policy_inexact_prune_bin() while xfrm_policy_lock is still held.

Conceptually, the change is:

if (bin && delete)
        __xfrm_policy_inexact_prune_bin(bin, false);
spin_unlock_bh(&net->xfrm.xfrm_policy_lock);

if (ret && delete)
        xfrm_policy_kill(ret);

The old wrapper xfrm_policy_inexact_prune_bin() became unused and was removed.

Exploit

After the sanitizer proof, I ran a separate non-KASAN exploitability pass. The test kernel was a vulnerable Linux 6.12.91 build in a QEMU snapshot VM, separate from the KASAN fuzzing kernel. I checked the non-KASAN surface by comparing kernel symbols: the normal fuzzing kernel exposed KASAN instrumentation symbols such as kasan_report and __asan_load*, while the exploit test kernel did not.

The attempt used the same reachable race: one thread group repeatedly changed SPD hash thresholds to rebuild the XFRM policy hash table, while another repeatedly added and deleted an inexact policy by selector. I launched it as uid/gid 65534 and relied on a user and network namespace for the required CAP_NET_ADMIN in that namespace. The run used 8 rebuild workers and 8 add/delete workers for 120 seconds.

That non-KASAN run did not produce a working exploit. The trigger reached XFRM netlink successfully and exited normally, but the guest stayed alive and the post-run serial log and dmesg contained no Oops, panic, warning, RCU stall, or general protection fault. I also did not observe a privilege transition or namespace escape.

The main obstacle was not reachability; it was obtaining a useful post-free reuse primitive. The vulnerable path uses the stale bin pointer shortly after xfrm_policy_kill(), while the competing free path releases the bin through RCU. In my non-KASAN run, I did not win a window where the backing xfrm_pol_inexact_bin was reclaimed and replaced with attacker-controlled data before the stale prune operation used it. Based on this attempt, I describe the bug as a confirmed UAF with unprivileged namespace reachability, but I do not claim a reliable local privilege-escalation or container-escape exploit.

Timeline

  • 2026-05-28: I recorded the candidate as an XFRM policy inexact-bin UAF.
  • 2026-06-09: The v2 patch was tracked locally as accepted/applied to the XFRM/IPsec tree.
  • 2026-06-25: CVE-2026-53239 was published by kernel.org/NVD.
  • 2026-06-28: The CVE record was updated with the CNA CVSS v3.1 score.
  • 2026-07-01: I published this note.

Mitigation

Upgrade to a kernel that includes the upstream or stable fix for CVE-2026-53239. The public CVE record references commit 7f2d76c9c03257c0782afef9d95321fa04096f60 and several stable backports.

References