SYLEN
AboutNewsConferenceMembershipDonate

Email updates

Conference, news, and membership updates by email.

Site

  • About
  • News
  • Membership
  • Waitlist
  • Donate

Conference

  • Conference 2027
  • Call for papers

Account

  • Create account
  • Membership details

SYLEN

  • Guidelines
  • Privacy
  • Terms

© 2026 Systems Leadership and Engineering Network. sylen.org.

Membership details →
Back to news
Systems ArchitectureSource: nebusec.aiJuly 8, 2026

GhostLock: Analyzing the 15-Year-Old Linux Kernel Stack Use-After-Free (CVE-2026-43499)

CVE-2026-43499 is a stack-based use-after-free vulnerability affecting the Linux kernel's rtmutex subsystem since version 2.6.39. The bug stems from an incorrect task reference cleanup during proxy lock rollback, allowing local unprivileged users to leave a dangling pointer on a thread's kernel stack.

Technical Overview and Impact

GhostLock, designated as CVE-2026-43499, is a high-severity kernel vulnerability introduced in 2011 with the rtmutex subsystem rework in commit 8161239a8bcc. The bug affects all major Linux distributions from kernel versions v2.6.39-rc1 to v7.1-rc1. Exploitation requires only `CONFIG_FUTEX_PI=y` to be enabled, needing no special privileges, user namespaces, or non-standard container capabilities. Security researchers demonstrated a highly stable privilege escalation and container escape using this flaw, earning a reward in Google's kernelCTF.

Root Cause in the rtmutex Subsystem

The core vulnerability lies in `remove_waiter()` within `kernel/locking/rtmutex.c`. This helper function was designed under the assumption that the thread calling it is always the owner of the waiter object. Consequently, it clears `current->pi_blocked_on` during cleanup.

This design assumption fails in the Requeue-PI proxy path. When `rt_mutex_start_proxy_lock()` is invoked, it enqueues an `rt_mutex_waiter` on behalf of a sleeping proxy task. If the proxy lock attempt fails and returns an error, such as `-EDEADLK`, the kernel executes a rollback by calling `remove_waiter()`.

Because `current` in this path is the requeueing thread (the caller of `FUTEX_CMP_REQUEUE_PI`) rather than the sleeping waiter thread, `remove_waiter()` clears `pi_blocked_on` for the wrong task. The actual sleeping waiter task retains its pointer to `rt_mutex_waiter`, which resides directly on its kernel stack. The patch addresses this by modifying the cleanup sequence to lock `waiter->task->pi_lock` and clear `waiter->task->pi_blocked_on` instead of modifying `current`.

Triggering the -EDEADLK Path

Reaching the buggy rollback path requires establishing a lock dependency cycle. This is constructed using three threads and three specific futexes:

  • `f_pi_chain`: A PI futex initially locked by the waiter thread.
  • `f_pi_target`: A PI futex initially locked by the owner thread, acting as the requeue target.
  • `f_wait`: A traditional futex where the waiter thread blocks.

The execution flow to trigger the deadlock rollback proceeds in a specific sequence:

  • The waiter thread acquires `f_pi_chain` and subsequently blocks on `f_wait` via a `FUTEX_WAIT_REQUEUE_PI` call, placing its `rt_mutex_waiter` on its kernel stack.
  • The owner thread acquires `f_pi_target` and blocks on `f_pi_chain`, which is held by the waiter thread.
  • The main thread executes `FUTEX_CMP_REQUEUE_PI` from `f_wait` to `f_pi_target`.
  • The kernel attempts to proxy lock the waiter onto `f_pi_target`. Because the owner of `f_pi_target` is blocked behind the waiter, a dependency cycle is closed: waiter to target, to owner, to chain, and back to waiter.
  • The deadlock detection engine identifies this cycle, returns `-EDEADLK`, and executes the flawed rollback.

The Stack Use-After-Free State

Once the rollback completes and the system resolves the deadlock, the waiter thread wakes up and returns to userspace. This return pops its kernel stack frame, effectively freeing the memory where the `rt_mutex_waiter` structure was located.

Because the cleanup cleared the requeuer's state instead of the waiter's state, the waiter's `pi_blocked_on` field still points to the now-deallocated stack frame. This creates a persistent stack use-after-free condition with no strict timing constraints. A subsequent system call that walks the priority inheritance chain, such as `sched_setattr()`, will traverse this dangling pointer. To control execution, an attacker must reclaim the popped stack frame with controlled data at the matching offset.

Read the original article at nebusec.ai.