CVE-2025-21693

mm: zswap: properly synchronize freeing resources during CPU hotunplug

High · published February 10, 2025

CVSS v3.1
7.8
EPSS
0%
Percentile
9.8
In the wild
Unconfirmed
What it is

In the Linux kernel, the following vulnerability has been resolved:

mm: zswap: properly synchronize freeing resources during CPU hotunplug

In zswap_compress() and zswap_decompress(), the per-CPU acomp_ctx of the

current CPU at the beginning of the operation is retrieved and used

throughout. However, since neither preemption nor migration are disabled,

it is possible that the operation continues on a different CPU.

If the original CPU is hotunplugged while the acomp_ctx is still in use,

we run into a UAF bug as some of the resources attached to the acomp_ctx

are freed during hotunplug in zswap_cpu_comp_dead() (i.e.

acomp_ctx.buffer, acomp_ctx.req, or acomp_ctx.acomp).

The problem was introduced in commit 1ec3b5fe6eec ("mm/zswap: move to use

crypto_acomp API for hardware acceleration") when the switch to the

crypto_acomp API was made. Prior to that, the per-CPU crypto_comp was

retrieved using get_cpu_ptr() which disables preemption and makes sure the

CPU cannot go away from under us. Preemption cannot be disabled with the

crypto_acomp API as a sleepable context is needed.

Use the acomp_ctx.mutex to synchronize CPU hotplug callbacks allocating

and freeing resources with compression/decompression paths. Make sure

that acomp_ctx.req is NULL when the resources are freed. In the

compression/decompression paths, check if acomp_ctx.req is NULL after

acquiring the mutex (meaning the CPU was offlined) and retry on the new

CPU.

The initialization of acomp_ctx.mutex is moved from the CPU hotplug

callback to the pool initialization where it belongs (where the mutex is

allocated). In addition to adding clarity, this makes sure that CPU

hotplug cannot reinitialize a mutex that is already locked by

compression/decompression.

Previously a fix was attempted by holding cpus_read_lock() [1]. This

would have caused a potential deadlock as it is possible for code already

holding the lock to fall into reclaim and enter zswap (causing a

deadlock). A fix was also attempted using SRCU for synchronization, but

Johannes pointed out that synchronize_srcu() cannot be used in CPU hotplug

notifiers [2].

Alternative fixes that were considered/attempted and could have worked:

- Refcounting the per-CPU acomp_ctx. This involves complexity in

handling the race between the refcount dropping to zero in

zswap_[de]compress() and the refcount being re-initialized when the

CPU is onlined.

- Disabling migration before getting the per-CPU acomp_ctx [3], but

that's discouraged and is a much bigger hammer than needed, and could

result in subtle performance issues.

[1]https://lkml.kernel.org/[email protected]/

[2]https://lkml.kernel.org/[email protected]/

[3]https://lkml.kernel.org/[email protected]/

[[email protected]: remove comment]

The record
Technical detail
CVSS v3.1
7.8 · HIGH
Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CVSS v4.0
Not supplied
EPSS
0.00203 · 9.8th percentile
Published
2025-02-10T15:58Z
EPSS history
Timeline
  • 10 FEB 15:58Z
    mm: zswap: properly synchronize freeing resources during CPU hotunplug
    cvelistv5