[PATCH 6.19 262/311] s390/zcrypt: Fix memory leak with CCA cards used as accelerator

Greg Kroah-Hartman
2026-04-08T19:01:03Z

From: Harald Freudenberger <freude@linux.ibm.com>

commit c8d46f17c2fc7d25c18e60c008928aecab26184d upstream.

Tests showed that there is a memory leak if CCA cards are used as
accelerator for clear key RSA requests (ME and CRT). With the last
rework for the memory allocation the AP messages are allocated by
ap_init_apmsg() but for some reason on two places (ME and CRT) the
older allocation was still in place. So the first allocation simple
was never freed.

Fixes: 57db62a130ce ("s390/ap/zcrypt: Rework AP message buffer allocation")
Reported-by: Yi Zhang <yi.zhang@redhat.com>
Closes: https://lore.kernel.org/linux-s390/CAHj4cs9H67Uz0iVaRQv447p7JFPRPy3TKAT4=Y6_e=wSHCZM5w@mail.gmail.com/
Reported-by: Nadja Hariz <Nadia.Hariz@ibm.com>
Cc: stable@vger.kernel.org
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/s390/crypto/zcrypt_msgtype6.c | 32 ++++++++++++++------------------
1 file changed, 14 insertions(+), 18 deletions(-)

--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -953,6 +953,10 @@ static atomic_t zcrypt_step = ATOMIC_INI
/*
* The request distributor calls this function if it picked the CEXxC
* device to handle a modexpo request.
+ * This function assumes that ap_msg has been initialized with
+ * ap_init_apmsg() and thus a valid buffer with the size of
+ * ap_msg->bufsize is available within ap_msg. Also the caller has
+ * to make sure ap_release_apmsg() is always called even on failure.
* @zq: pointer to zcrypt_queue structure that identifies the
* CEXxC device to the request distributor
* @mex: pointer to the modexpo request buffer
@@ -964,21 +968,17 @@ static long zcrypt_msgtype6_modexpo(stru
struct ap_response_type *resp_type = &ap_msg->response;
int rc;

- ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL);
- if (!ap_msg->msg)
- return -ENOMEM;
- ap_msg->bufsize = PAGE_SIZE;
ap_msg->receive = zcrypt_msgtype6_receive;
ap_msg->psmid = (((unsigned long)current->pid) << 32) +
atomic_inc_return(&zcrypt_step);
rc = icamex_msg_to_type6mex_msgx(zq, ap_msg, mex);
if (rc)
- goto out_free;
+ goto out;
resp_type->type = CEXXC_RESPONSE_TYPE_ICA;
init_completion(&resp_type->work);
rc = ap_queue_message(zq->queue, ap_msg);
if (rc)
- goto out_free;
+ goto out;
rc = wait_for_completion_interruptible(&resp_type->work);
if (rc == 0) {
rc = ap_msg->rc;
@@ -991,15 +991,17 @@ static long zcrypt_msgtype6_modexpo(stru
ap_cancel_message(zq->queue, ap_msg);
}

-out_free:
- free_page((unsigned long)ap_msg->msg);
- ap_msg->msg = NULL;
+out:
return rc;
}

/*
* The request distributor calls this function if it picked the CEXxC
* device to handle a modexpo_crt request.
+ * This function assumes that ap_msg has been initialized with
+ * ap_init_apmsg() and thus a valid buffer with the size of
+ * ap_msg->bufsize is available within ap_msg. Also the caller has
+ * to make sure ap_release_apmsg() is always called even on failure.
* @zq: pointer to zcrypt_queue structure that identifies the
* CEXxC device to the request distributor
* @crt: pointer to the modexpoc_crt request buffer
@@ -1011,21 +1013,17 @@ static long zcrypt_msgtype6_modexpo_crt(
struct ap_response_type *resp_type = &ap_msg->response;
int rc;

- ap_msg->msg = (void *)get_zeroed_page(GFP_KERNEL);
- if (!ap_msg->msg)
- return -ENOMEM;
- ap_msg->bufsize = PAGE_SIZE;
ap_msg->receive = zcrypt_msgtype6_receive;
ap_msg->psmid = (((unsigned long)current->pid) << 32) +
atomic_inc_return(&zcrypt_step);
rc = icacrt_msg_to_type6crt_msgx(zq, ap_msg, crt);
if (rc)
- goto out_free;
+ goto out;
resp_type->type = CEXXC_RESPONSE_TYPE_ICA;
init_completion(&resp_type->work);
rc = ap_queue_message(zq->queue, ap_msg);
if (rc)
- goto out_free;
+ goto out;
rc = wait_for_completion_interruptible(&resp_type->work);
if (rc == 0) {
rc = ap_msg->rc;
@@ -1038,9 +1036,7 @@ static long zcrypt_msgtype6_modexpo_crt(
ap_cancel_message(zq->queue, ap_msg);
}

-out_free:
- free_page((unsigned long)ap_msg->msg);
- ap_msg->msg = NULL;
+out:
return rc;
}