s390/zcrypt: Improve CCA CPRB length and overflow checks

The xcrb_msg_to_type6cprb_msgx() function lacks proper input
validation, creating security vulnerabilities:
1. Integer overflow after CEIL4 alignment: Signed int variables could
   overflow during 4-byte boundary alignment, causing undersized
   buffer allocations or incorrect bounds checking.
2. Missing minimum size validation: The CPRBX structure is copied from
   userspace without verifying sufficient buffer length. Undersized
   buffers cause uninitialized memory access when reading structure
   fields like cprbx.cprb_len and cprbx.domain.
3. Arithmetic overflow in sum calculations: Adding control block and
   data block sizes could overflow, bypassing size checks and enabling
   buffer overflows.

Fix by using size_t for length calculations, adding U32_MAX boundary
checks after alignment, validating minimum control block size before
copying from userspace, and detecting sum calculation overflows.

Fixes: e2c6d91eb8 ("s390/zcrypt: Rework domain processing within zcrypt device driver")
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Cc: stable@vger.kernel.org # 7.1+
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
Harald Freudenberger 2026-08-03 10:33:35 +02:00 committed by Vasily Gorbik
parent 5004889551
commit 35ac6754ba

View File

@ -342,49 +342,39 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
};
} __packed * msg = ap_msg->msg;
int rcblen = CEIL4(xcrb->request_control_blk_length);
int req_sumlen, resp_sumlen;
char *req_data = ap_msg->msg + sizeof(struct type6_hdr) + rcblen;
char *function_code;
size_t req_cblen, rep_cblen, req_sumlen, rep_sumlen;
char *function_code, *req_data;
if (CEIL4(xcrb->request_control_blk_length) <
xcrb->request_control_blk_length)
return -EINVAL; /* overflow after alignment*/
/* length checks */
ap_msg->len = sizeof(struct type6_hdr) +
CEIL4(xcrb->request_control_blk_length) +
xcrb->request_data_length;
/* request length and overflow checks */
if (xcrb->request_control_blk_length < sizeof(struct CPRBX))
return -EINVAL;
req_cblen = CEIL4((size_t)xcrb->request_control_blk_length);
if (req_cblen > U32_MAX)
return -EINVAL;
req_sumlen = req_cblen + xcrb->request_data_length;
if (req_sumlen > U32_MAX)
return -EINVAL;
ap_msg->len = sizeof(struct type6_hdr) + req_sumlen;
if (ap_msg->len > ap_msg->bufsize)
return -EINVAL;
/*
* Overflow check
* sum must be greater (or equal) than the largest operand
*/
req_sumlen = CEIL4(xcrb->request_control_blk_length) +
xcrb->request_data_length;
if ((CEIL4(xcrb->request_control_blk_length) <=
xcrb->request_data_length) ?
if (req_cblen <= xcrb->request_data_length ?
req_sumlen < xcrb->request_data_length :
req_sumlen < CEIL4(xcrb->request_control_blk_length)) {
req_sumlen < req_cblen) {
return -EINVAL;
}
if (CEIL4(xcrb->reply_control_blk_length) <
xcrb->reply_control_blk_length)
return -EINVAL; /* overflow after alignment*/
/*
* Overflow check
* sum must be greater (or equal) than the largest operand
*/
resp_sumlen = CEIL4(xcrb->reply_control_blk_length) +
xcrb->reply_data_length;
if ((CEIL4(xcrb->reply_control_blk_length) <=
xcrb->reply_data_length) ?
resp_sumlen < xcrb->reply_data_length :
resp_sumlen < CEIL4(xcrb->reply_control_blk_length)) {
/* reply length and overflow checks */
if (xcrb->reply_control_blk_length < sizeof(struct CPRBX))
return -EINVAL;
rep_cblen = CEIL4((size_t)xcrb->reply_control_blk_length);
if (rep_cblen > U32_MAX)
return -EINVAL;
rep_sumlen = rep_cblen + xcrb->reply_data_length;
if (rep_sumlen > U32_MAX)
return -EINVAL;
if (rep_cblen <= xcrb->reply_data_length ?
rep_sumlen < xcrb->reply_data_length :
rep_sumlen < rep_cblen) {
return -EINVAL;
}
@ -393,7 +383,7 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
memcpy(msg->hdr.agent_id, &xcrb->agent_ID, sizeof(xcrb->agent_ID));
msg->hdr.tocardlen1 = xcrb->request_control_blk_length;
if (xcrb->request_data_length) {
msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
msg->hdr.offset2 = msg->hdr.offset1 + req_cblen;
msg->hdr.tocardlen2 = xcrb->request_data_length;
}
msg->hdr.fromcardlen1 = xcrb->reply_control_blk_length;
@ -404,8 +394,8 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
xcrb->request_control_blk_addr,
xcrb->request_control_blk_length))
return -EFAULT;
if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
xcrb->request_control_blk_length)
/* copy subfunction code into AP msg type 6 function code field */
if (msg->cprbx.cprb_len > req_cblen - sizeof(msg->hdr.function_code))
return -EINVAL;
function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
memcpy(msg->hdr.function_code, function_code,
@ -437,10 +427,13 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
}
/* copy data block */
if (xcrb->request_data_length &&
z_copy_from_user(userspace, req_data, xcrb->request_data_address,
xcrb->request_data_length))
return -EFAULT;
if (xcrb->request_data_length) {
req_data = ap_msg->msg + sizeof(struct type6_hdr) + req_cblen;
if (z_copy_from_user(userspace, req_data,
xcrb->request_data_address,
xcrb->request_data_length))
return -EFAULT;
}
return 0;
}