GhostLock: The 15-Year-Old Linux Kernel Bug AI Found Hiding in Plain Sight

TL;DR

An AI-powered security agent discovered a Linux kernel vulnerability that has existed in every major distribution since 2011. It allows any unprivileged local user to gain full root access with a 97% success rate. There is no practical mitigation — patch your kernel.

AttributeDetail
CVECVE-2026-43499
CVSS 3.17.8 (HIGH) — AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWECWE-416 (Use After Free)
AffectedLinux 2.6.39 through 7.0
FixedLinux 7.1 (commit 3bfdc63936dd)
Stable backports6.1.177, 6.6.144, 6.12.95, 6.18.36, 7.0.13
Not backported5.15.y, 5.10.y LTS
Discovered byVEGA AI Agent (Nebula Security)
Bounty$92,337 (Google kernelCTF)
PrerequisitesLocal access, no special privileges, CONFIG_FUTEX_PI=y

What Is GhostLock?

GhostLock (CVE-2026-43499) is a use-after-free vulnerability in the Linux kernel’s real-time mutex (rtmutex) priority-inheritance code. Specifically, it lives in the remove_waiter() function in kernel/locking/rtmutex.c.

The bug was introduced in Linux 2.6.39 (released in 2011) and was fixed in Linux 7.1. For over 15 years, it sat in the kernel’s scheduling subsystem — the code responsible for managing which processes get access to CPU time and in what order.

The Root Cause

The remove_waiter() function was written to clean up after a thread that blocks on a lock. It assumed that current (the thread currently executing) is always the same thread that owns the waiter object being cleaned up.

This assumption held for the normal slow-path — where a thread blocks on its own and cleans up after itself. But when the futex requeue-PI path was added, remove_waiter() started being called on behalf of a different, sleeping thread. In that path, current is the thread that issued FUTEX_CMP_REQUEUE_PI, not the thread that owns the waiter.

The result: remove_waiter() clears pi_blocked_on on the wrong task. The actual waiter task keeps pi_blocked_on pointing at a stack frame that gets freed when the waiter returns to userspace. Any subsequent priority-inheritance chain walk through that task follows a dangling pointer into freed kernel stack memory.

Why Lockdep Didn’t Catch It

Linux’s lockdep validator only checks that a pi_lock is held — it doesn’t verify whose lock it is. So the bug passed every automated lock-correctness check the kernel has for 15 years.


How to Trigger It

The exploit requires three futex words and three threads:

  1. f_pi_chain — a PI futex, locked by the waiter thread
  2. f_pi_target — a PI futex, locked by the owner thread (the requeue target)
  3. f_wait — a plain futex the waiter blocks on via FUTEX_WAIT_REQUEUE_PI

The sequence:

  1. The waiter takes f_pi_chain, then blocks in FUTEX_WAIT_REQUEUE_PI(f_wait → f_pi_target). Its rt_mutex_waiter object is now on its kernel stack.
  2. The owner takes f_pi_target, then blocks on f_pi_chain (which the waiter holds).
  3. The main thread calls FUTEX_CMP_REQUEUE_PI(f_wait → f_pi_target).

The requeue tries to proxy the waiter onto f_pi_target. The owner of f_pi_target is already blocked behind the waiter through f_pi_chain, so the chain walk detects a deadlock cycle: waiter → f_pi_target → owner → f_pi_chain → waiter. It returns -EDEADLK and triggers the buggy rollback.

The waiter wakes with a dangling pi_blocked_on. The UAF window is now wide open — there’s no time pressure. The attacker can trigger a follow-up sched_setattr() that walks the PI chain and dereferences the dangling pointer whenever they want.

Note that three threads is for better understanding. To win the race and trigger UAF, you only need one CPU core.


The Exploit Chain

Nebula Security’s exploit works as follows:

  1. Prefetch ASLR Leak — Uses CPU cache timing to leak the kernel image slide and physmap base address, defeating KASLR.
  2. Trigger GhostLock — Stage the three-futex cycle to leave the dangling rt_mutex_waiter pointer.
  3. Stack Reuse via PR_SET_MM_MAP — Spray controlled bytes onto the freed kernel stack using the prctl(PR_SET_MM_MAP) syscall, forging a fake rt_mutex_waiter structure.
  4. Controlled Write — The forged waiter passes kernel integrity checks and yields a constrained arbitrary write primitive.
  5. Hijack inet6_protos[IPPROTO_UDP] — Use the write to modify the IPv6 protocol function table, redirecting a function pointer.
  6. DirtyMode — Pivot from the function table hijack to STATIC_USERMODE_HELPER overwrite, achieving arbitrary code execution as root.

Success rate: 97% during testing. The exploit also works as a container escape — a privileged container can break out to the host kernel.


What’s Affected

ComponentStatus
Linux 2.6.39 through 7.0Vulnerable
Linux 7.1+Patched
Stable backports: 6.1.177, 6.6.144, 6.12.95, 6.18.36, 7.0.13Patched
5.15.y and 5.10.y LTSNot patched (no backport)
Debian (testing)Fixed in 7.0.4-1
Proxmox VE 9Fixed in 7.0.14-4-pve
NixOSFixed in 6.18.38
Amazon Linux 2023Fixed on all streams
Amazon Linux 2Terminal (end-of-life)
RHEL 8/9/10 standard kernelStill affected (no RHSA yet)
CloudLinuxAll supported versions fixed

Partial Mitigations (Not Fixes)

MitigationEffect
RANDOMIZE_KSTACK_OFFSETMakes stack reuse a 1/32 guess instead of deterministic. Still exploitable with retries.
STATIC_USERMODE_HELPERCloses this specific DirtyMode path but the technique generalizes to other /proc/sys knobs.

Neither is a substitute for patching.


The AI Angle

The vulnerability was discovered by VEGA, an AI bug-hunting agent developed by Nebula Security. VEGA was designed to autonomously explore kernel code paths and identify potential vulnerabilities.

This isn’t the first AI-found bug in 2026. In June, Anthropic’s Mythos model identified vulnerabilities in classified US government systems during a testing exercise — NSA chief Joshua Rudd told Senator Mark Warner that Mythos “broke into almost all of our classified systems, not in weeks, but in hours.”

The pattern is clear: AI tools are finding bugs that human code review missed for 15 years. The question isn’t whether AI will replace security researchers — it’s how fast the gap between AI discovery and attacker adoption will close now that PoC code is public.


Proof of Concept

Multiple PoC implementations exist on GitHub:

1. MobiusM/CVE-2026-43499 (Primary PoC)

  • Repo: github.com/MobiusM/CVE-2026-43499
  • Stars: 99 | Forks: 26
  • Language: C (91.8%), CMake (8.2%)
  • Status: Working trigger — causes kernel panic on vulnerable devices
  • Target: Android (uses NDK + adb), but the technique applies to any Linux system
  • What it does: Triggers the UAF condition. Does not include the full privilege escalation chain — it demonstrates the vulnerability by crashing the kernel. The full exploit (root + container escape) is described in Nebula’s research but not all code is public.

Build & run:

cmake -B build -G Ninja
cmake --build build
adb push build/trigger /data/local/tmp/trigger
adb shell chmod +x /data/local/tmp/trigger
adb shell /data/local/tmp/trigger

Expected output on a vulnerable device ends with a kernel panic: Unable to handle kernel write to read-only memory

2. tc3650/CVE-2026-43499-armv7

  • Repo: github.com/tc3650/CVE-2026-43499-armv7
  • Target: ARMv7 devices (Android phones, IoT)
  • Includes: Multiple exploit variants (8-iov spray, 3-iov variants, offset scanning)
  • Status: More complete — includes variations targeting different kernel versions

3. pubglite55/oppo-ghostlock

4. Nebula Security’s Full Writeup

  • URL: nebusec.ai/research/ionstack-part-2
  • Includes: Complete technical analysis of the exploit chain, ASLR bypass, stack reuse technique, and DirtyMode escalation
  • The Patch: Shows the one-line fix — remove_waiter() uses waiter->task instead of current

5. Distro Patch-Status Tracker

  • Repo: github.com/suominen/ghostlock
  • What it does: Auto-updated tracker showing which distributions have backported the fix
  • Maintained by: Claude Code (AI-maintained)

The Fix

The patch is elegantly simple. In remove_waiter(), instead of operating on current, it operates on waiter->task — the actual task that owns the waiter:

// BEFORE (vulnerable):
raw_spin_lock(&current->pi_lock);
rt_mutex_dequeue(lock, waiter);
current->pi_blocked_on = NULL;        // Wrong task!
raw_spin_unlock(&current->pi_lock);

// AFTER (fixed):
struct task_struct *waiter_task = waiter->task;
scoped_guard(raw_spinlock, &waiter_task->pi_lock) {
    rt_mutex_dequeue(lock, waiter);
    waiter_task->pi_blocked_on = NULL;  // Correct task
}

Commit: 3bfdc63936dd — “rtmutex: Use waiter::task instead of current in remove_waiter()”


Action Items

  1. Patch immediately if running Linux 2.6.39 through 7.0 on any internet-facing or multi-tenant system
  2. Check your kernel version: uname -r — if it’s below 7.1 (or the stable backports listed above), you’re vulnerable
  3. Prioritize: Cloud servers, containers, CI runners, and shared/multi-tenant hosts where local access is easier to obtain
  4. RHEL users: No RHSA exists yet for the standard kernel on RHEL 8/9/10. Monitor for updates and consider running a patched kernel from other sources if exposure is critical
  5. 5.15.y / 5.10.y LTS users: No backport exists. If you’re on these LTS branches, you need to upgrade to a newer kernel branch

Timeline

DateEvent
2011 (Linux 2.6.39)Bug introduced in rtmutex rework
2026-04-18Nebula Security reports bug to [email protected]
2026-04-20Bug fixed with initial patch
2026-05-04Fix backported to stable branches
2026-06-30Google acknowledges kernelCTF submission, awards $92,337
2026-07-07Nebula publishes full technical writeup
2026-07-12Cybernews publishes public coverage

Sources