GhostLock: 15‑Year‑Old Stack UAF Vulnerability in All Linux Distributions (CVE‑2026‑43499)

GhostLock (CVE‑2026‑43499) is a stack‑use‑after‑free bug that has existed in every major Linux distribution for over 15 years, can be triggered without special kernel configuration, and enables a high‑reliability privilege‑escalation and container‑escape chain that the authors detail step‑by‑step, including mitigation patches and a timeline of disclosure.

Black & White Path
Black & White Path
Black & White Path
GhostLock: 15‑Year‑Old Stack UAF Vulnerability in All Linux Distributions (CVE‑2026‑43499)

Vulnerability Overview

GhostLock (CVE‑2026‑43499) is a Linux kernel use‑after‑free in the PI futex implementation. It exists from v2.6.39‑rc1 to v7.1‑rc1 and requires only CONFIG_FUTEX_PI=y. The bug is introduced by commit 8161239a8bcc ("rtmutex: Simplify PI algorithm and make highest prio task get lock") and fixed by commit 3bfdc63936dd ("rtmutex: Use waiter::task instead of current in remove_waiter()").

Root Cause

The helper remove_waiter() clears current->pi_blocked_on under the assumption that the current task is the waiter being removed. When the function is invoked from rt_mutex_start_proxy_lock() during a FUTEX_REQUEUE_PI operation, current is the re‑queuer, not the sleeping waiter. The rollback therefore clears the wrong task's pi_blocked_on, leaving the waiter’s pi_blocked_on pointing to its own stack frame, which has already been freed.

Triggering the -EDEADLK Path

Thread waiter acquires f_pi_chain and blocks on FUTEX_WAIT_REQUEUE_PI; its rt_mutex_waiter lives on its kernel stack.

Thread owner acquires f_pi_target and then blocks on f_pi_chain.

The main thread issues FUTEX_CMP_REQUEUE_PI on f_wait, re‑queueing the waiter to f_pi_target.

This creates the cycle waiter → f_pi_target → owner → f_pi_chain → waiter. The traversal detects a deadlock, rt_mutex_start_proxy_lock() returns -EDEADLK, and the buggy rollback leaves a dangling pi_blocked_on in the waiter.

Three‑thread futex deadlock triggering GhostLock UAF
Three‑thread futex deadlock triggering GhostLock UAF

Exploiting the Stack Use‑After‑Free

After the rollback the waiter returns to user space with a pointer to its freed stack frame. The attacker can spray controlled data onto the same stack depth to reclaim the object.

Three threads are shown for clarity; a single CPU core is sufficient to win the race.

Initial Primitive

Write an arbitrary (constrained) pointer to an address.

Write eight zero bytes to an arbitrary (constrained) address.

Both primitives pass internal integrity checks and the kernel returns normally.

Reusing the Stack with PR_SET_MM_MAP

The attacker invokes prctl(PR_SET_MM, PR_SET_MM_MAP, ...) to copy a crafted auxv buffer onto the freed stack frame, reconstructing a fake rt_mutex_waiter:

struct rt_mutex_waiter {
  struct rt_waiter_node tree;
  struct rt_waiter_node pi_tree;
  struct task_struct *task;
  struct rt_mutex_base *lock;
  unsigned int wake_state;
  struct ww_acquire_ctx *ww_ctx;
};

The auxv layout is chosen so that: tree becomes an rb‑node whose removal promotes a chosen child pointer to the root. task is set to &init_task (a valid task_struct) to make dereferences safe. lock points to &inet6_protos[IPPROTO_UDP] - 8, aligning the write target. wake_state is set to 0.

A race between a sibling thread calling fallocate(PUNCH_HOLE) and the prctl call extends the copy_from_user window, allowing precise placement of the controlled data.

From Fake Waiter to a Constrained Write

The fake waiter does not give an arbitrary write; the PI chain traversal performs a single constrained store:

task->pi_blocked_on = forged_waiter;
forged_waiter->lock = forged_rt_mutex_base;
rt_mutex_dequeue(lock, waiter); // rb_erase writes child pointer into root slot

The lock field is set to target - 8, so the rb‑erase writes into target, which is the waiters.rb_root.rb_node field. The write is a 64‑bit store *(uint64_t *)target = W0_BASE. The preceding qword must read as an unlocked spinlock (low 4 bytes zero) or the traversal aborts.

Choosing a Writable Target: inet6_protos[IPPROTO_UDP]

Scanning writable data reveals many pointer tables; inet6_protos[IPPROTO_UDP] satisfies the required layout:

inet6_protos[16] == NULL          // fake wait_lock (unlocked)
inet6_protos[17] == &udpv6_protocol // target (IPPROTO_UDP)
inet6_protos[18] == NULL          // fake rb_leftmost
inet6_protos[19] == NULL          // fake owner

After the write, inet6_protos[IPPROTO_UDP] points to a crafted inet6_protocol structure:

struct inet6_protocol {
  int (*handler)(struct sk_buff *skb);
  int (*err_handler)(...);
  unsigned int flags;
};

Sending a loopback IPv6 UDP packet causes the kernel to dereference handler, giving the attacker PC control.

Pivot and DirtyMode

The attacker packs multiple objects into the same 104‑byte CEA (CPU Entry Area) window: a forged inet6_protocol, several JOP/pivot slots, and the final ROP stack. On the Google lts‑6.12.80 kernel an additional load/store is needed to place the CEA address into rbp before pivoting with mov rsp, rbp; pop rbp; ret.

Instead of a full ROP chain, a "DirtyMode" primitive flips a permission bit in core_pattern 's mode field (sysctl). Setting the second LSB to writable allows the attacker to write |/proc/%P/fd/666 %P to /proc/sys/kernel/core_pattern, causing a helper to execute with root privileges.

Mitigation

Patches

diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1544,6 +1544,8 @@
  static bool rt_mutex_spin_on_owner(struct rt_mutex_base *lock,
  *
  * Must be called with lock->wait_lock held and interrupts disabled. It must
  * have just failed to try_to_take_rt_mutex().
  */
+ * When invoked from rt_mutex_start_proxy_lock() waiter::task != current !
+ */
   static void __sched remove_waiter(struct rt_mutex_base *lock,
           struct rt_mutex_waiter *waiter)
@@ -1551,14 +1553,15 @@
   bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock));
   struct task_struct *owner = rt_mutex_owner(lock);
+  struct task_struct *waiter_task = waiter->task;
   struct rt_mutex_base *next_lock;
   lockdep_assert_held(&lock->wait_lock);
-  raw_spin_lock(&current->pi_lock);
-  rt_mutex_dequeue(lock, waiter);
-  current->pi_blocked_on = NULL;
-  raw_spin_unlock(&current->pi_lock);
+  scoped_guard(raw_spinlock, &waiter_task->pi_lock) {
+    rt_mutex_dequeue(lock, waiter);
+    waiter_task->pi_blocked_on = NULL;
+  }
   /*
    * Only update priority if the waiter was the highest priority
@@ -1594,7 +1597,7 @@
   raw_spin_unlock_irq(&lock->wait_lock);
   rt_mutex_adjust_prio_chain(owner, RT_MUTEX_MIN_CHAINWALK, lock,
-            next_lock, NULL, current);
+            next_lock, NULL, waiter_task);
*** End of Patch ***

Kernel Config Options

RANDOMIZE_KSTACK_OFFSET

: when enabled the stack‑reuse step requires a 1/32 guess of the offset. STATIC_USERMODE_HELPER: disables the DirtyMode path by protecting the ctl_table::mode field in /proc/sys.

Timeline

2026‑04‑18: Bug reported to [email protected] with patch draft.

2026‑04‑20: Bug fixed with a second patch.

2026‑05‑04: Fix back‑ported to v1.

2026‑06‑30: Google confirmed the kernelCTF submission.

2026‑07‑07: Blog post published.

References

Full exploit code: https://github.com/NebuSec/CyberMeowfia/tree/main/IonStack/CVE-2026-43499

Original article: https://nebusec.ai/research/ionstack-part-2/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Linux Kernelprivilege escalationcontainer escapertmutexkernel exploitationCVE-2026-43499GhostLockstack UAF
Black & White Path
Written by

Black & White Path

We are the beacon of the cyber world, a stepping stone on the road to security.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.