]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
s390/zcrypt: Improve CCA CPRB length and overflow checks
authorHarald Freudenberger <freude@linux.ibm.com>
Mon, 3 Aug 2026 08:33:35 +0000 (10:33 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Wed, 5 Aug 2026 13:12:22 +0000 (15:12 +0200)
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: e2c6d91eb8b1 ("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>
drivers/s390/crypto/zcrypt_msgtype6.c

index 40f72cdf284d3e3b295a7316c18105245165255a..3e19e79d747cf0880012f8ba36348d46ffd9d507 100644 (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;
 }