In early 2025, a significant vulnerability was discovered and patched in the Linux kernel’s memory subsystem, specifically involving the zswap feature during CPU hotunplug operations. Known as CVE-2025-21693, this bug could allow for use-after-free (UAF) conditions due to improper resource management and synchronization when CPUs are dynamically removed ("hotunplugged"). Below, I'll break down the vulnerability, illustrate code snippets to clarify risk, discuss failed previous fixes, and offer details on successful exploitation.

1. Brief Background: zswap and CPU Hotplug

- zswap: A compressed swap cache in the Linux kernel. It helps systems avoid expensive I/O by storing compressed data in RAM.
- CPU Hotplug: Lets you add or remove CPUs on a live system (for power/thermal management, maintenance, etc.).
- Per-CPU Data: For performance reasons, zswap keeps a special data structure (acomp_ctx) per CPU for compression.

2. Where the Bug Lived: Technical Exploration

In Linux kernel commit 1ec3b5fe6eec, zswap switched to using the crypto_acomp API to get hardware acceleration for compressing and decompressing pages. While older code safely pinned a thread to a CPU (get_cpu_ptr() disables preemption), the new API *requires* a sleepable context. That means threads can move between CPUs at any time.

What went wrong?

- zswap_compress() and zswap_decompress() fetch acomp_ctx for the *current CPU* once at the start.
- If the thread moves to another CPU, or if the originating CPU is "hot-unplugged," the *old* CPU's acomp_ctx could be freed by the kernel.
- But the compress/decompress path might still be using it => Use-After-Free bug!

*Simplified Vulnerable Code Snippet*

// Fetch per-CPU context at the start
struct acomp_ctx *ctx = this_cpu_ptr(&acomp_ctx);

zswap_compress(ctx, ...);
// [ctx potentially gets freed if CPU is hotunplugged while in use]

*What Should Have Happened?*

Proper checks or locks are needed so the context is never freed while in use.

Several fixes were tried

- Hold cpus_read_lock() (Attempt 1): Prevents CPUs from being hotplugged, but this can deadlock if reclaim code (which might already hold the lock) enters zswap.
- Use SRCU for synchronization (Attempt 2): But, synchronize_srcu() is not safe in the CPU hotplug notifier context.
- Reference counting per-CPU contexts: Gets tricky because of race conditions between dropping and picking up references as CPUs come and go.
- Disable migrations (Attempt 3): Not allowed with crypto_acomp, and it would hurt performance.

CPU hotplug callbacks (which free resources)

- Compression/Decompression functions (which use the resources)

The mutex is initialized at pool setup, not in the hotplug thread.

- Before freeing resources, the mutex is *held*. So, compress/decompress cannot use resources while they're being torn down.

Here’s a simplified illustration

// In zswap_compress() and zswap_decompress()
mutex_lock(&ctx->mutex);

if (!ctx->req) {
    // This CPU was offlined during our operation.
    mutex_unlock(&ctx->mutex);
    // Retry on the new CPU
    goto retry;
}

// ...use ctx->req and other resources...

mutex_unlock(&ctx->mutex);

And in the CPU hotplug removal path (zswap_cpu_comp_dead())

mutex_lock(&ctx->mutex);
free(ctx->buffer);
free(ctx->req);
crypto_free_acomp(ctx->acomp);
ctx->req = NULL;
mutex_unlock(&ctx->mutex);

This ensures mutual exclusion: The context can't be used and freed at the same time.

A use-after-free like this has real-world implications

- Data corruption: Compress or decompress could operate on freed memory, leading to random behavior, panics, or lost data.
- Privilege escalation: If an attacker can coordinate process migration and CPU unplug while holding sensitive data in swap, it may result in reading stale or cross-process data (hard but plausible).

Proof-of-Concept Skeleton

_Prerequisite_: You need to repeatedly hot-unplug a CPU while compressing/decompressing in zswap. Here's a skeleton approach (not full exploit code, but gives you an idea):

# Suppose CPU 2 is online
echo  > /sys/devices/system/cpu/cpu2/online  # hotunplug CPU 2

# In another thread, cause zswap activity
stress --vm N  # repeatedly stress swapping

Meanwhile, kernel traces might show

BUG: KASAN: use-after-free in zswap_compress+xNNN
...

Note: Attacker must have root or equivalent privilege to mess with CPU hotplug on most systems, which is why this bug is serious but likely not a remote privilege escalation vector.

6. References & Further Reading

1. Original report and patch discussion (LKML)
2. Alternative fix via SRCU commentary
3. Migration disable proposal
4. Git commit introducing crypto_acomp

Summary:
CVE-2025-21693 shows how subtle changes in kernel resource management–especially for per-CPU data and hotplug support–can open up serious bugs. The fix uses straightforward mutexes and NULL checking to prevent freeing in-use resources. This one’s a great case study in Linux synchronization gone wrong... and then right.

If you're a kernel developer or a sysadmin running swap-heavy workloads with CPU hotplug, make sure you're running a kernel with this patch applied.


*Exclusively written for you. For more deep-dive Linux CVE analysis, follow this space!*

Timeline

Published on: 02/10/2025 16:15:38 UTC
Last modified on: 02/10/2025 18:15:35 UTC