Skip to content

Commit f07d352

Browse files
committed
Merge: CVE-2025-21693 mm: zswap: properly synchronize freeing resources during CPU hotunplug
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/6880 JIRA: https://issues.redhat.com/browse/RHEL-78678 CVE: CVE-2025-21693 Backport of upstream commit 12dcb0e ("mm: zswap: properly synchronize freeing resources during CPU hotunplug") and its follow-up fixes to address the tagged CVE. Signed-off-by: Rafael Aquini <raquini@redhat.com> Approved-by: Herton R. Krzesinski <herton@redhat.com> Approved-by: Aristeu Rozanski <arozansk@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Augusto Caringi <acaringi@redhat.com>
2 parents 134517e + e080412 commit f07d352

File tree

1 file changed

+84
-33
lines changed

1 file changed

+84
-33
lines changed

mm/zswap.c

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -710,35 +710,41 @@ static int zswap_cpu_comp_prepare(unsigned int cpu, struct hlist_node *node)
710710
{
711711
struct zswap_pool *pool = hlist_entry(node, struct zswap_pool, node);
712712
struct crypto_acomp_ctx *acomp_ctx = per_cpu_ptr(pool->acomp_ctx, cpu);
713-
struct crypto_acomp *acomp;
714-
struct acomp_req *req;
713+
struct crypto_acomp *acomp = NULL;
714+
struct acomp_req *req = NULL;
715+
u8 *buffer = NULL;
715716
int ret;
716717

717-
mutex_init(&acomp_ctx->mutex);
718-
719-
acomp_ctx->buffer = kmalloc_node(PAGE_SIZE * 2, GFP_KERNEL, cpu_to_node(cpu));
720-
if (!acomp_ctx->buffer)
721-
return -ENOMEM;
718+
buffer = kmalloc_node(PAGE_SIZE * 2, GFP_KERNEL, cpu_to_node(cpu));
719+
if (!buffer) {
720+
ret = -ENOMEM;
721+
goto fail;
722+
}
722723

723724
acomp = crypto_alloc_acomp_node(pool->tfm_name, 0, 0, cpu_to_node(cpu));
724725
if (IS_ERR(acomp)) {
725726
pr_err("could not alloc crypto acomp %s : %ld\n",
726727
pool->tfm_name, PTR_ERR(acomp));
727728
ret = PTR_ERR(acomp);
728-
goto acomp_fail;
729+
goto fail;
729730
}
730-
acomp_ctx->acomp = acomp;
731731

732-
req = acomp_request_alloc(acomp_ctx->acomp);
732+
req = acomp_request_alloc(acomp);
733733
if (!req) {
734734
pr_err("could not alloc crypto acomp_request %s\n",
735735
pool->tfm_name);
736736
ret = -ENOMEM;
737-
goto req_fail;
737+
goto fail;
738738
}
739-
acomp_ctx->req = req;
740739

740+
/*
741+
* Only hold the mutex after completing allocations, otherwise we may
742+
* recurse into zswap through reclaim and attempt to hold the mutex
743+
* again resulting in a deadlock.
744+
*/
745+
mutex_lock(&acomp_ctx->mutex);
741746
crypto_init_wait(&acomp_ctx->wait);
747+
742748
/*
743749
* if the backend of acomp is async zip, crypto_req_done() will wakeup
744750
* crypto_wait_req(); if the backend of acomp is scomp, the callback
@@ -747,31 +753,77 @@ static int zswap_cpu_comp_prepare(unsigned int cpu, struct hlist_node *node)
747753
acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
748754
crypto_req_done, &acomp_ctx->wait);
749755

756+
acomp_ctx->buffer = buffer;
757+
acomp_ctx->acomp = acomp;
758+
acomp_ctx->req = req;
759+
mutex_unlock(&acomp_ctx->mutex);
750760
return 0;
751761

752-
req_fail:
753-
crypto_free_acomp(acomp_ctx->acomp);
754-
acomp_fail:
755-
kfree(acomp_ctx->buffer);
762+
fail:
763+
if (acomp)
764+
crypto_free_acomp(acomp);
765+
kfree(buffer);
756766
return ret;
757767
}
758768

759769
static int zswap_cpu_comp_dead(unsigned int cpu, struct hlist_node *node)
760770
{
761771
struct zswap_pool *pool = hlist_entry(node, struct zswap_pool, node);
762772
struct crypto_acomp_ctx *acomp_ctx = per_cpu_ptr(pool->acomp_ctx, cpu);
773+
struct acomp_req *req;
774+
struct crypto_acomp *acomp;
775+
u8 *buffer;
763776

764-
if (!IS_ERR_OR_NULL(acomp_ctx)) {
765-
if (!IS_ERR_OR_NULL(acomp_ctx->req))
766-
acomp_request_free(acomp_ctx->req);
767-
if (!IS_ERR_OR_NULL(acomp_ctx->acomp))
768-
crypto_free_acomp(acomp_ctx->acomp);
769-
kfree(acomp_ctx->buffer);
770-
}
777+
if (IS_ERR_OR_NULL(acomp_ctx))
778+
return 0;
779+
780+
mutex_lock(&acomp_ctx->mutex);
781+
req = acomp_ctx->req;
782+
acomp = acomp_ctx->acomp;
783+
buffer = acomp_ctx->buffer;
784+
acomp_ctx->req = NULL;
785+
acomp_ctx->acomp = NULL;
786+
acomp_ctx->buffer = NULL;
787+
mutex_unlock(&acomp_ctx->mutex);
788+
789+
/*
790+
* Do the actual freeing after releasing the mutex to avoid subtle
791+
* locking dependencies causing deadlocks.
792+
*/
793+
if (!IS_ERR_OR_NULL(req))
794+
acomp_request_free(req);
795+
if (!IS_ERR_OR_NULL(acomp))
796+
crypto_free_acomp(acomp);
797+
kfree(buffer);
771798

772799
return 0;
773800
}
774801

802+
static struct crypto_acomp_ctx *acomp_ctx_get_cpu_lock(struct zswap_pool *pool)
803+
{
804+
struct crypto_acomp_ctx *acomp_ctx;
805+
806+
for (;;) {
807+
acomp_ctx = raw_cpu_ptr(pool->acomp_ctx);
808+
mutex_lock(&acomp_ctx->mutex);
809+
if (likely(acomp_ctx->req))
810+
return acomp_ctx;
811+
/*
812+
* It is possible that we were migrated to a different CPU after
813+
* getting the per-CPU ctx but before the mutex was acquired. If
814+
* the old CPU got offlined, zswap_cpu_comp_dead() could have
815+
* already freed ctx->req (among other things) and set it to
816+
* NULL. Just try again on the new CPU that we ended up on.
817+
*/
818+
mutex_unlock(&acomp_ctx->mutex);
819+
}
820+
}
821+
822+
static void acomp_ctx_put_unlock(struct crypto_acomp_ctx *acomp_ctx)
823+
{
824+
mutex_unlock(&acomp_ctx->mutex);
825+
}
826+
775827
/*********************************
776828
* pool functions
777829
**********************************/
@@ -1029,7 +1081,7 @@ static struct zswap_pool *zswap_pool_create(char *type, char *compressor)
10291081
struct zswap_pool *pool;
10301082
char name[38]; /* 'zswap' + 32 char (max) num + \0 */
10311083
gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN | __GFP_KSWAPD_RECLAIM;
1032-
int ret;
1084+
int ret, cpu;
10331085

10341086
if (!zswap_has_pool) {
10351087
/* if either are unset, pool initialization failed, and we
@@ -1067,6 +1119,9 @@ static struct zswap_pool *zswap_pool_create(char *type, char *compressor)
10671119
goto error;
10681120
}
10691121

1122+
for_each_possible_cpu(cpu)
1123+
mutex_init(&per_cpu_ptr(pool->acomp_ctx, cpu)->mutex);
1124+
10701125
ret = cpuhp_state_add_instance(CPUHP_MM_ZSWP_POOL_PREPARE,
10711126
&pool->node);
10721127
if (ret)
@@ -1376,9 +1431,7 @@ static void __zswap_load(struct zswap_entry *entry, struct page *page)
13761431
struct crypto_acomp_ctx *acomp_ctx;
13771432
u8 *src;
13781433

1379-
acomp_ctx = raw_cpu_ptr(entry->pool->acomp_ctx);
1380-
mutex_lock(&acomp_ctx->mutex);
1381-
1434+
acomp_ctx = acomp_ctx_get_cpu_lock(entry->pool);
13821435
src = zpool_map_handle(zpool, entry->handle, ZPOOL_MM_RO);
13831436
if (!zpool_can_sleep_mapped(zpool)) {
13841437
memcpy(acomp_ctx->buffer, src, entry->length);
@@ -1392,10 +1445,10 @@ static void __zswap_load(struct zswap_entry *entry, struct page *page)
13921445
acomp_request_set_params(acomp_ctx->req, &input, &output, entry->length, PAGE_SIZE);
13931446
BUG_ON(crypto_wait_req(crypto_acomp_decompress(acomp_ctx->req), &acomp_ctx->wait));
13941447
BUG_ON(acomp_ctx->req->dlen != PAGE_SIZE);
1395-
mutex_unlock(&acomp_ctx->mutex);
13961448

13971449
if (zpool_can_sleep_mapped(zpool))
13981450
zpool_unmap_handle(zpool, entry->handle);
1451+
acomp_ctx_put_unlock(acomp_ctx);
13991452
}
14001453

14011454
/*********************************
@@ -1612,9 +1665,7 @@ bool zswap_store(struct folio *folio)
16121665
}
16131666

16141667
/* compress */
1615-
acomp_ctx = raw_cpu_ptr(entry->pool->acomp_ctx);
1616-
1617-
mutex_lock(&acomp_ctx->mutex);
1668+
acomp_ctx = acomp_ctx_get_cpu_lock(entry->pool);
16181669

16191670
dst = acomp_ctx->buffer;
16201671
sg_init_table(&input, 1);
@@ -1662,7 +1713,7 @@ bool zswap_store(struct folio *folio)
16621713
buf = zpool_map_handle(zpool, handle, ZPOOL_MM_WO);
16631714
memcpy(buf, dst, dlen);
16641715
zpool_unmap_handle(zpool, handle);
1665-
mutex_unlock(&acomp_ctx->mutex);
1716+
acomp_ctx_put_unlock(acomp_ctx);
16661717

16671718
/* populate entry */
16681719
entry->swpentry = swp_entry(type, offset);
@@ -1705,7 +1756,7 @@ bool zswap_store(struct folio *folio)
17051756
return true;
17061757

17071758
put_dstmem:
1708-
mutex_unlock(&acomp_ctx->mutex);
1759+
acomp_ctx_put_unlock(acomp_ctx);
17091760
put_pool:
17101761
zswap_pool_put(entry->pool);
17111762
freepage:

0 commit comments

Comments
 (0)