All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling
@ 2026-07-30 14:15 Harald Freudenberger
  2026-07-30 14:15 ` [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:15 UTC (permalink / raw)
  To: dengler, fcallies, ifranzki
  Cc: freude, linux-s390, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev

Rework the limit and overflow checks in the both functions
xcrb_msg_to_type6_ep11cprb_msgx() and xcrb_msg_to_type6cprb_msgx().
And more ... all related to the length checks done in these both
functions.

Changelog:

v1: initial version, one patch dealing with overflow checks around
    the use of the CEIL4 macro in zcrypt_msgtype6.c
v2: Sashiko found a regression: The check
      if (ap_msg->len > ap_msg->bufsize)
    was missing in the rework. Added again this important check.
v3: - Split into two patches  - one for CCA one for EP11
    - rework upper limit/overflow check 
    - add lower limit check
v4: - tried to address all the remaining complains from sashiko.
v5: - Yea, sashiko still found out that the min req size needs
      to take the minimal payload header into account. Fixed.
v6: - And finally add padding zeros when there is a gap between
      user space provided message size and rounded message size
      in kernel.
v7: - As requested by Holger, the padding code is now an own patch.
    - As the length checks are now so strict the allocation in the
      both misc files needed adaption to correctly allocate 4 byte
      rounded up message buffers.
    - The EP11 length check has been found to be a mess. The payload
      is asn1 encoded and now the length parses parts of this payload
      to extract the needed fields (function value).
    - Furthermore another function zcrypt_msgtype6_send_ep11_cprb()
      also needed to be adjusted to parse the asn1 payload to patch
      the domain value into the payload.
v8: - Sashiko found out that the cprb free with memory scrub also
      needs adaption when the allocation rounds up to a 4 byte
      boundary. So fixed just this in the first patch of the series. 
v9: - hunk for cprb free with 4 byte boundary for EP11 was missing.
    - use of put_unaligned_be16/32 instead of direct cast and store.
v10: - make the asn1 parsing bullet proof. Sashiko found that under
       some circumstances there could have been an access behind the
       actual payload.
     - Added Cc and Fixes tags.

Harald Freudenberger (5):
  s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
  s390/zcrypt: Improve CCA CPRB length and overflow checks
  s390/zcrypt: Improve EP11 CPRB length and overflow checks
  s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
  s390/zcrypt: Pad trailing CCA or EP11 message with zeros

 drivers/s390/crypto/zcrypt_ccamisc.c  |  20 +-
 drivers/s390/crypto/zcrypt_ep11misc.c |  16 +-
 drivers/s390/crypto/zcrypt_msgtype6.c | 365 ++++++++++++++++++--------
 3 files changed, 278 insertions(+), 123 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
  2026-07-30 14:15 [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
@ 2026-07-30 14:15 ` Harald Freudenberger
  2026-07-30 14:25   ` sashiko-bot
  2026-07-30 14:15 ` [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:15 UTC (permalink / raw)
  To: dengler, fcallies, ifranzki
  Cc: freude, linux-s390, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev

Both CPRB alloc functions in zcrypt_ccamisc.c and zcrypt_ep11misc.c
did not round up the memory allocation to a multiple of 4 bytes as it
is needed by the zcrypt layer to process the CPRBs.

Now the alloc_and_prep_cprbmem() and alloc_cprbmem() functions
guarantee that the base CPRB struct and a possible parameter block are
aligned to a 4-byte boundary and the backing memory allocation is
rounded up to the next multiple of 4 byte. Also the free_cprbmem() is
updated and scrubs the rounded up amount of memory.

Fixes: 9bdb5f7e8369 ("s390/zcrypt: Introduce cprb mempool for cca misc functions")
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Cc: stable@vger.kernel.org # 6.16+
---
 drivers/s390/crypto/zcrypt_ccamisc.c  | 20 ++++++++++++++------
 drivers/s390/crypto/zcrypt_ep11misc.c | 16 +++++++++++-----
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
index 322677a8d320..16be61e92ce3 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/random.h>
+#include <linux/align.h>
 #include <asm/zcrypt.h>
 #include <asm/pkey.h>
 
@@ -231,6 +232,10 @@ EXPORT_SYMBOL(cca_check_sececckeytoken);
  * block, reply CPRB and reply param block and fill in values
  * for the common fields. Returns 0 on success or errno value
  * on failure.
+ * It is guaranteed that request and a possible param block
+ * are aligned to a 4 byte boundary. Furthermore if a param
+ * block is used, the memory allocated for this is rounded up to
+ * the next multiple of 4 bytes.
  */
 static int alloc_and_prep_cprbmem(size_t paramblen,
 				  u8 **p_cprb_mem,
@@ -239,7 +244,8 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
 				  u32 xflags)
 {
 	u8 *cprbmem = NULL;
-	size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen;
+	size_t cprbplusparamblen =
+		ALIGN(sizeof(struct CPRBX), 4) + ALIGN(paramblen, 4);
 	size_t len = 2 * cprbplusparamblen;
 	struct CPRBX *preqcblk, *prepcblk;
 
@@ -266,10 +272,10 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
 	memcpy(preqcblk->func_id, "T2", 2);
 	preqcblk->rpl_msgbl = cprbplusparamblen;
 	if (paramblen) {
-		preqcblk->req_parmb =
-			((u8 __user *)preqcblk) + sizeof(struct CPRBX);
-		preqcblk->rpl_parmb =
-			((u8 __user *)prepcblk) + sizeof(struct CPRBX);
+		preqcblk->req_parmb = ((u8 __user *)preqcblk) +
+			ALIGN(sizeof(struct CPRBX), 4);
+		preqcblk->rpl_parmb = ((u8 __user *)prepcblk) +
+			ALIGN(sizeof(struct CPRBX), 4);
 	}
 
 	*p_cprb_mem = cprbmem;
@@ -287,8 +293,10 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
  */
 static void free_cprbmem(void *mem, size_t paramblen, bool scrub, u32 xflags)
 {
+	size_t cprblen = ALIGN(sizeof(struct CPRBX), 4) + ALIGN(paramblen, 4);
+
 	if (mem && scrub)
-		memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen));
+		memzero_explicit(mem, 2 * cprblen);
 
 	if (xflags & ZCRYPT_XFLAG_NOMEMALLOC)
 		mempool_free(mem, cprb_mempool);
diff --git a/drivers/s390/crypto/zcrypt_ep11misc.c b/drivers/s390/crypto/zcrypt_ep11misc.c
index 3dda9589f2b9..2d900ffc5068 100644
--- a/drivers/s390/crypto/zcrypt_ep11misc.c
+++ b/drivers/s390/crypto/zcrypt_ep11misc.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/random.h>
 #include <linux/slab.h>
+#include <linux/align.h>
 #include <asm/zcrypt.h>
 #include <asm/pkey.h>
 #include <crypto/aes.h>
@@ -355,21 +356,24 @@ EXPORT_SYMBOL(ep11_check_aes_key);
 
 /*
  * Allocate and prepare ep11 cprb plus additional payload.
+ * It is guaranteed that the memory is aligned to a 4 byte boundary.
+ * Furthermore the memory allocation is rounded up to the next
+ * multiple of 4 bytes (with taking the payload_len into account).
  */
 static void *alloc_cprbmem(size_t payload_len, u32 xflags)
 {
-	size_t len = sizeof(struct ep11_cprb) + payload_len;
+	size_t memlen = ALIGN(sizeof(struct ep11_cprb) + payload_len, 4);
 	struct ep11_cprb *cprb = NULL;
 
 	if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) {
-		if (len <= CPRB_MEMPOOL_ITEM_SIZE)
+		if (memlen <= CPRB_MEMPOOL_ITEM_SIZE)
 			cprb = mempool_alloc_preallocated(cprb_mempool);
 	} else {
-		cprb = kmalloc(len, GFP_KERNEL);
+		cprb = kmalloc(memlen, GFP_KERNEL);
 	}
 	if (!cprb)
 		return NULL;
-	memset(cprb, 0, len);
+	memset(cprb, 0, memlen);
 
 	cprb->cprb_len = sizeof(struct ep11_cprb);
 	cprb->cprb_ver_id = 0x04;
@@ -385,8 +389,10 @@ static void *alloc_cprbmem(size_t payload_len, u32 xflags)
  */
 static void free_cprbmem(void *mem, size_t payload_len, bool scrub, u32 xflags)
 {
+	size_t memlen = ALIGN(sizeof(struct ep11_cprb) + payload_len, 4);
+
 	if (mem && scrub)
-		memzero_explicit(mem, sizeof(struct ep11_cprb) + payload_len);
+		memzero_explicit(mem, memlen);
 
 	if (xflags & ZCRYPT_XFLAG_NOMEMALLOC)
 		mempool_free(mem, cprb_mempool);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks
  2026-07-30 14:15 [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
  2026-07-30 14:15 ` [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
@ 2026-07-30 14:15 ` Harald Freudenberger
  2026-07-30 14:48   ` sashiko-bot
  2026-07-30 14:15 ` [PATCH v10 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:15 UTC (permalink / raw)
  To: dengler, fcallies, ifranzki
  Cc: freude, linux-s390, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev

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>
Cc: stable@vger.kernel.org # 7.1+
---
 drivers/s390/crypto/zcrypt_msgtype6.c | 78 +++++++++++++--------------
 1 file changed, 36 insertions(+), 42 deletions(-)

diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 40f72cdf284d..fb37e28c8242 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -342,49 +342,40 @@ 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 */
+	/* 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;
 	ap_msg->len = sizeof(struct type6_hdr) +
-		CEIL4(xcrb->request_control_blk_length) +
-		xcrb->request_data_length;
+		req_cblen + xcrb->request_data_length;
 	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) ?
+	req_sumlen = req_cblen + xcrb->request_data_length;
+	if (req_sumlen > U32_MAX)
+		return -EINVAL;
+	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 +384,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 +395,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 +428,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;
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 3/5] s390/zcrypt: Improve EP11 CPRB length and overflow checks
  2026-07-30 14:15 [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
  2026-07-30 14:15 ` [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
  2026-07-30 14:15 ` [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
@ 2026-07-30 14:15 ` Harald Freudenberger
  2026-07-30 14:35   ` sashiko-bot
  2026-07-30 14:15 ` [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
  2026-07-30 14:15 ` [PATCH v10 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
  4 siblings, 1 reply; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:15 UTC (permalink / raw)
  To: dengler, fcallies, ifranzki
  Cc: freude, linux-s390, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev

The xcrb_msg_to_type6_ep11cprb_msgx() function lacks proper input
validation, creating security vulnerabilities:
1. Missing minimum size validation: The ep11_cprb structure and
   subsequent payload fields (pld_tag, pld_lenfmt) are copied from
   userspace without verifying sufficient buffer length.
2. Arithmetic overflow in length calculations: CEIL4 alignment could
   overflow, bypassing size checks and enabling buffer overflows.
3. The payload is asn1 encoded but the function just uses a simple c
   struct overlay to access some fields of the payload.

Fix by using size_t for length calculations, adding U32_MAX boundary
checks after alignment, and validating minimum request size and
minimum reply size before copying from userspace. Do a very simple
asn1 parsing of the payload up to the function value field.

Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver")
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Cc: stable@vger.kernel.org # 7.1+
---
 drivers/s390/crypto/zcrypt_msgtype6.c | 152 +++++++++++++++++++-------
 1 file changed, 113 insertions(+), 39 deletions(-)

diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index fb37e28c8242..c351ead03072 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/atomic.h>
 #include <linux/uaccess.h>
+#include <linux/unaligned.h>
 
 #include "ap_bus.h"
 #include "zcrypt_api.h"
@@ -34,6 +35,9 @@
 #define CEXXC_RESPONSE_TYPE_XCRB 1
 #define CEXXC_RESPONSE_TYPE_EP11 2
 
+/* smallest possible EP11 payload size */
+#define MIN_EP11_PAYLOAD_SIZE 5
+
 MODULE_AUTHOR("IBM Corporation");
 MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
 		   "Copyright IBM Corp. 2001, 2023");
@@ -439,12 +443,59 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
 	return 0;
 }
 
+/*
+ * Simple asn1 int reader/decoder helper function
+ * Returns number of bytes processed or < 0 on failure
+ * Only accepts int length values of 1, 2 or 4.
+ */
+static inline int asn1_int_decode(const u8 *buf, size_t intlen, u32 *u)
+{
+	switch (intlen) {
+	case 1:
+		*u = (u32)(*buf);
+		return 1;
+	case 2:
+		*u = (u32)get_unaligned_be16(buf);
+		return 2;
+	case 4:
+		*u = (u32)get_unaligned_be32(buf);
+		return 4;
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * Simple asn1 length parse helper function
+ * Returns number of bytes processed or < 0 on failure
+ * Only accepts length encoded within the length octet
+ * or for long form 1, 2 or 4 octet length bytes.
+ */
+static inline int asn1_length_decode(const u8 *buf, size_t buflen, u32 *u)
+{
+	int i;
+
+	if (buflen < 1)
+		return -EINVAL;
+
+	if (*buf < 128) {
+		*u = (u32)(*buf & 0x7F);
+		return 1;
+	}
+
+	i = *buf & 0x7F;
+	if (--buflen < i)
+		return -EINVAL;
+	i = asn1_int_decode(++buf, i, u);
+
+	return i < 0 ? i : i + 1;
+}
+
 static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap_msg,
 					   struct ep11_urb *xcrb,
 					   unsigned int *fcode,
 					   unsigned int *domain)
 {
-	unsigned int lfmt;
 	static struct type6_hdr static_type6_ep11_hdr = {
 		.type		=  0x06,
 		.rqid		= {0x00, 0x01},
@@ -456,34 +507,32 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap
 	struct {
 		struct type6_hdr hdr;
 		union {
-			struct {
-				struct ep11_cprb cprbx;
-				unsigned char pld_tag;    /* fixed value 0x30 */
-				unsigned char pld_lenfmt; /* length format */
-			} __packed;
+			struct ep11_cprb cprbx;
 			DECLARE_FLEX_ARRAY(u8, userdata);
 		};
 	} __packed * msg = ap_msg->msg;
 
-	struct pld_hdr {
-		unsigned char	func_tag;	/* fixed value 0x4 */
-		unsigned char	func_len;	/* fixed value 0x4 */
-		unsigned int	func_val;	/* function ID	   */
-		unsigned char	dom_tag;	/* fixed value 0x4 */
-		unsigned char	dom_len;	/* fixed value 0x4 */
-		unsigned int	dom_val;	/* domain id	   */
-	} __packed * payload_hdr = NULL;
-
-	if (CEIL4(xcrb->req_len) < xcrb->req_len)
-		return -EINVAL; /* overflow after alignment*/
+	size_t req_len, rep_len, pld_len;
+	unsigned char *pld;
+	int offs = 0, i;
+	unsigned int u;
 
-	/* length checks */
-	ap_msg->len = sizeof(struct type6_hdr) + CEIL4(xcrb->req_len);
+	/* request length and overflow checks */
+	if (xcrb->req_len < sizeof(struct ep11_cprb) + MIN_EP11_PAYLOAD_SIZE)
+		return -EINVAL;
+	req_len = CEIL4(xcrb->req_len);
+	if (req_len < xcrb->req_len || req_len > U32_MAX)
+		return -EINVAL;
+	ap_msg->len = sizeof(struct type6_hdr) + req_len;
 	if (ap_msg->len > ap_msg->bufsize)
 		return -EINVAL;
 
-	if (CEIL4(xcrb->resp_len) < xcrb->resp_len)
-		return -EINVAL; /* overflow after alignment*/
+	/* reply length and overflow checks */
+	if (xcrb->resp_len < sizeof(struct ep11_cprb))
+		return -EINVAL;
+	rep_len = CEIL4(xcrb->resp_len);
+	if (rep_len < xcrb->resp_len || rep_len > U32_MAX)
+		return -EINVAL;
 
 	/* prepare type6 header */
 	msg->hdr = static_type6_ep11_hdr;
@@ -492,26 +541,51 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap
 
 	/* Import CPRB data from the ioctl input parameter */
 	if (z_copy_from_user(userspace, msg->userdata,
-			     (char __force __user *)xcrb->req, xcrb->req_len)) {
+			     (char __force __user *)xcrb->req, xcrb->req_len))
 		return -EFAULT;
-	}
 
-	if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
-		switch (msg->pld_lenfmt & 0x03) {
-		case 1:
-			lfmt = 2;
-			break;
-		case 2:
-			lfmt = 3;
-			break;
-		default:
-			return -EINVAL;
-		}
-	} else {
-		lfmt = 1; /* length format #1 */
-	}
-	payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt);
-	*fcode = payload_hdr->func_val & 0xFFFF;
+	pld = msg->userdata + sizeof(struct ep11_cprb);
+	pld_len = msg->cprbx.payload_len;
+	if (pld_len != xcrb->req_len - sizeof(struct ep11_cprb))
+		return -EINVAL;
+	/*
+	 * At this point pld_len is always >= MIN_EP11_PAYLOAD_SIZE
+	 * and the smallest supported asn1 payload is:
+	 *   payload tag (1 octet)
+	 *   payload length (1-5 octets)
+	 *   function tag (1 octet)
+	 *   function length (1-5 octets)
+	 *   function value (1-4 octets)
+	 */
+
+	/* payload tag */
+	if (pld[offs++] != 0x30)
+		return -EINVAL;
+	/* payload length field */
+	i = asn1_length_decode(pld + offs, pld_len - offs, &u);
+	if (i < 0)
+		return -EINVAL;
+	offs += i;
+	if (offs > pld_len || u > pld_len - offs)
+		return -EINVAL;
+	/* function tag */
+	if (pld[offs++] != 0x04)
+		return -EINVAL;
+	/* function length */
+	if (offs > pld_len)
+		return -EINVAL;
+	i = asn1_length_decode(pld + offs, pld_len - offs, &u);
+	if (i < 0)
+		return -EINVAL;
+	offs += i;
+	if (offs > pld_len || u > pld_len - offs)
+		return -EINVAL;
+	/* function value */
+	i = asn1_int_decode(pld + offs, u, &u);
+	if (i < 0)
+		return -EINVAL;
+	offs += i;
+	*fcode = 0xFFFF & u;
 
 	/* enable special processing based on the cprbs flags special bit */
 	if (msg->cprbx.flags & 0x20)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
  2026-07-30 14:15 [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
                   ` (2 preceding siblings ...)
  2026-07-30 14:15 ` [PATCH v10 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
@ 2026-07-30 14:15 ` Harald Freudenberger
  2026-07-30 14:29   ` sashiko-bot
  2026-07-30 14:15 ` [PATCH v10 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
  4 siblings, 1 reply; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:15 UTC (permalink / raw)
  To: dengler, fcallies, ifranzki
  Cc: freude, linux-s390, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev

The zcrypt_msgtype6_send_ep11_cprb() function uses fragile struct
overlays to access and modify the domain field in the EP11 CPRB
payload, creating maintainability and security concerns:
1. Struct overlay approach (pld_hdr) assumes fixed payload structure
   and doesn't validate the actual ASN.1 encoding.
2. Complex length format detection logic is error-prone and doesn't
   properly validate bounds at each parsing step.
3. Direct struct member access bypasses proper ASN.1 validation.

Fix by replacing struct overlays with explicit ASN.1 parsing that
validates each field (payload tag/length, function tag/length/value,
optional domain tag/length/value) with proper bounds checking at every
step. Add asn1_int_encode() helper function to safely write integer
values with correct endianness conversion. This makes the code
consistent with the validation pattern introduced with the rework of
the xcrb_msg_to_type6_ep11cprb_msgx() function.

Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver")
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Cc: stable@vger.kernel.org # 7.1+
---
 drivers/s390/crypto/zcrypt_msgtype6.c | 127 +++++++++++++++++++-------
 1 file changed, 96 insertions(+), 31 deletions(-)

diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index c351ead03072..02705f184ef8 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -1227,6 +1227,28 @@ int prep_ep11_ap_msg(bool userspace, struct ep11_urb *xcrb,
 					       func_code, domain);
 }
 
+/*
+ * Simple asn1 int writer/encoder helper function
+ * Returns number of bytes processed or < 0 on failure
+ * Only accepts int length values of 1, 2 or 4.
+ */
+static inline int asn1_int_encode(u8 *buf, size_t intlen, u32 u)
+{
+	switch (intlen) {
+	case 1:
+		*buf = (u8)u;
+		return 1;
+	case 2:
+		put_unaligned_be16((u16)u, buf);
+		return 2;
+	case 4:
+		put_unaligned_be32((u32)u, buf);
+		return 4;
+	default:
+		return -EINVAL;
+	}
+}
+
 /*
  * The request distributor calls this function if it picked the CEX4P
  * device to handle a send_ep11_cprb request.
@@ -1239,51 +1261,94 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue *
 					   struct ap_message *ap_msg)
 {
 	int rc;
-	unsigned int lfmt;
 	struct ap_response_type *resp_type = &ap_msg->response;
 	struct {
 		struct type6_hdr hdr;
 		struct ep11_cprb cprbx;
-		unsigned char	pld_tag;	/* fixed value 0x30 */
-		unsigned char	pld_lenfmt;	/* payload length format */
 	} __packed * msg = ap_msg->msg;
-	struct pld_hdr {
-		unsigned char	func_tag;	/* fixed value 0x4 */
-		unsigned char	func_len;	/* fixed value 0x4 */
-		unsigned int	func_val;	/* function ID	   */
-		unsigned char	dom_tag;	/* fixed value 0x4 */
-		unsigned char	dom_len;	/* fixed value 0x4 */
-		unsigned int	dom_val;	/* domain id	   */
-	} __packed * payload_hdr = NULL;
 
 	/*
 	 * The target domain field within the cprb body/payload block will be
 	 * replaced by the usage domain for non-management commands only.
 	 * Therefore we check the first bit of the 'flags' parameter for
 	 * management command indication.
-	 *   0 - non management command
-	 *   1 - management command
 	 */
-	if (!((msg->cprbx.flags & 0x80) == 0x80)) {
-		msg->cprbx.target_id = (unsigned int)
-					AP_QID_QUEUE(zq->queue->qid);
-
-		if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
-			switch (msg->pld_lenfmt & 0x03) {
-			case 1:
-				lfmt = 2;
-				break;
-			case 2:
-				lfmt = 3;
-				break;
-			default:
+	if (!(msg->cprbx.flags & 0x80)) {
+		int i, offs = 0;
+		size_t pld_len;
+		u8 *pld;
+		u32 u;
+
+		/* update target field in ep11_cprb */
+		msg->cprbx.target_id = (u32)AP_QID_QUEUE(zq->queue->qid);
+
+		/* ptr and length to payload */
+		pld = ap_msg->msg +
+			sizeof(struct type6_hdr) + sizeof(struct ep11_cprb);
+		pld_len = msg->cprbx.payload_len;
+		if (pld_len < MIN_EP11_PAYLOAD_SIZE)
+			return -EINVAL;
+
+		/*
+		 * Parse the asn1 payload, at least we have
+		 *   pld tag (1 octet)
+		 *   payload length (1-5 octets)
+		 *   function tag (1 octet)
+		 *   function length (1-5 octets)
+		 *   function value (1-4 octets)
+		 *   ----- optional fields -----
+		 *   domain tag (1 octet)
+		 *   domain length (1-5 octets)
+		 *   domain value (1-4 octets)
+		 *   ... maybe much more data ...
+		 */
+
+		/* payload tag */
+		if (pld[offs++] != 0x30)
+			return -EINVAL;
+		/* payload length field */
+		i = asn1_length_decode(pld + offs, pld_len - offs, &u);
+		if (i < 0)
+			return -EINVAL;
+		offs += i;
+		if (offs > pld_len || u > pld_len - offs)
+			return -EINVAL;
+		/* function tag */
+		if (pld[offs++] != 0x04)
+			return -EINVAL;
+		/* function length */
+		if (offs > pld_len)
+			return -EINVAL;
+		i = asn1_length_decode(pld + offs, pld_len - offs, &u);
+		if (i < 0)
+			return -EINVAL;
+		offs += i;
+		if (u > pld_len - offs)
+			return -EINVAL;
+		/* skip over the function value */
+		offs += u;
+		/* is there some payload left which could hold a domain value ? */
+		if (offs < pld_len && pld_len - offs >= 3) {
+			/* domain tag */
+			if (pld[offs++] != 0x04)
 				return -EINVAL;
-			}
-		} else {
-			lfmt = 1; /* length format #1 */
+			/* domain length */
+			i = asn1_length_decode(pld + offs, pld_len - offs, &u);
+			if (i < 0)
+				return -EINVAL;
+			offs += i;
+			if (offs > pld_len || u > pld_len - offs)
+				return -EINVAL;
+			/*
+			 * pld[offs] is now at the start of the domain value
+			 * with the value sprawled in u octets.
+			 */
+			i = asn1_int_encode(pld + offs, u,
+					    AP_QID_QUEUE(zq->queue->qid));
+			if (i < 0)
+				return -EINVAL;
+			offs += i;
 		}
-		payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt);
-		payload_hdr->dom_val = AP_QID_QUEUE(zq->queue->qid);
 	}
 
 	/*
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros
  2026-07-30 14:15 [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
                   ` (3 preceding siblings ...)
  2026-07-30 14:15 ` [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
@ 2026-07-30 14:15 ` Harald Freudenberger
  2026-07-30 14:29   ` sashiko-bot
  4 siblings, 1 reply; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:15 UTC (permalink / raw)
  To: dengler, fcallies, ifranzki
  Cc: freude, linux-s390, Heiko Carstens, Vasily Gorbik,
	Alexander Gordeev

The both functions xcrb_msg_to_type6cprb_msgx() and
xcrb_msg_to_type6_ep11cprb_msgx() copy the user space message into a
kernel buffer based on the message length. But on further processing
the message is supposed to be 4 byte length adjusted. Thus up to 3
bytes of uninitialized kernel memory are forwarded to further
processing steps and may unwanted expose kernel memory to the crypto
card firmware.

This patch contains code to pad the gap between user space copied
message and message buffer length sent down to further processing of
the CCA or EP11 message to zeros.

Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver")
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Cc: stable@vger.kernel.org # 7.1+
---
 drivers/s390/crypto/zcrypt_msgtype6.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index 02705f184ef8..e11505248c05 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -399,6 +399,10 @@ 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;
+	/* pad tail with 0 up to req_cblen */
+	if (xcrb->request_control_blk_length < req_cblen)
+		memset(msg->userdata + xcrb->request_control_blk_length,
+		       0, req_cblen - 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;
@@ -543,6 +547,10 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap
 	if (z_copy_from_user(userspace, msg->userdata,
 			     (char __force __user *)xcrb->req, xcrb->req_len))
 		return -EFAULT;
+	/* pad tail with 0 up to req_len */
+	if (xcrb->req_len < req_len)
+		memset(msg->userdata + xcrb->req_len, 0,
+		       req_len - xcrb->req_len);
 
 	pld = msg->userdata + sizeof(struct ep11_cprb);
 	pld_len = msg->cprbx.payload_len;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
  2026-07-30 14:15 ` [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
@ 2026-07-30 14:25   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-30 14:25 UTC (permalink / raw)
  To: Harald Freudenberger
  Cc: linux-s390, Vasily Gorbik, Heiko Carstens, Alexander Gordeev

> Both CPRB alloc functions in zcrypt_ccamisc.c and zcrypt_ep11misc.c
> did not round up the memory allocation to a multiple of 4 bytes as it
> is needed by the zcrypt layer to process the CPRBs.
> 
> Now the alloc_and_prep_cprbmem() and alloc_cprbmem() functions
> guarantee that the base CPRB struct and a possible parameter block are
> aligned to a 4-byte boundary and the backing memory allocation is
> rounded up to the next multiple of 4 byte. Also the free_cprbmem() is
> updated and scrubs the rounded up amount of memory.
> 
> Fixes: 9bdb5f7e8369 ("s390/zcrypt: Introduce cprb mempool for cca misc functions")
> Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260730141509.205970-1-freude@linux.ibm.com?part=1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
  2026-07-30 14:15 ` [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
@ 2026-07-30 14:29   ` sashiko-bot
  2026-07-30 14:43     ` Harald Freudenberger
  0 siblings, 1 reply; 12+ messages in thread
From: sashiko-bot @ 2026-07-30 14:29 UTC (permalink / raw)
  To: Harald Freudenberger
  Cc: Heiko Carstens, Vasily Gorbik, linux-s390, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] An off-by-one error in bounds checking allows a 1-byte out-of-bounds read when parsing the ASN.1 function tag.
--

--- Patch [4]: [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing ---
commit 195849014f55379e7fe6dcd2f7156dd9744b4047
Author: Harald Freudenberger <freude@linux.ibm.com>

s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing

This commit replaces struct overlays in EP11 CPRB domain handling with
explicit ASN.1 parsing to improve security and maintainability. It adds
bounds checking and a new helper function for safe integer encoding.

> diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
> --- a/drivers/s390/crypto/zcrypt_msgtype6.c
> +++ b/drivers/s390/crypto/zcrypt_msgtype6.c

[ ... ]

> @@ -1239,51 +1261,94 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue *

[ ... ]

> +		/* payload tag */
> +		if (pld[offs++] != 0x30)
> +			return -EINVAL;
> +		/* payload length field */
> +		i = asn1_length_decode(pld + offs, pld_len - offs, &u);
> +		if (i < 0)
> +			return -EINVAL;
> +		offs += i;
> +		if (offs > pld_len || u > pld_len - offs)
> +			return -EINVAL;

[Severity: High]
Does this allow a 1-byte out-of-bounds read of the pld buffer if the ASN.1
payload length field uses a multi-byte encoding that exactly consumes the
remaining pld_len bytes?

If offs exactly equals pld_len, the bounds check offs > pld_len will evaluate
to false. If u is 0, u > pld_len - offs becomes 0 > 0, which is also false.

> +		/* function tag */
> +		if (pld[offs++] != 0x04)

The code would then proceed to read pld[offs++], reading one byte past the
end of the pld buffer.

Should this bounds check use >= instead?

if (offs >= pld_len || u > pld_len - offs)

Additionally, looking at xcrb_msg_to_type6_ep11cprb_msgx() in the same file:

	/* payload length field */
	i = asn1_length_decode(pld + offs, pld_len - offs, &u);
	if (i < 0)
		return -EINVAL;
	offs += i;
	if (offs > pld_len || u > pld_len - offs)
		return -EINVAL;
	/* function tag */
	if (pld[offs++] != 0x04)

This is a pre-existing issue, but does it suffer from the identical bounds
checking flaw?

> +			return -EINVAL;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260730141509.205970-1-freude@linux.ibm.com?part=4

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros
  2026-07-30 14:15 ` [PATCH v10 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
@ 2026-07-30 14:29   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-30 14:29 UTC (permalink / raw)
  To: Harald Freudenberger
  Cc: Alexander Gordeev, linux-s390, Heiko Carstens, Vasily Gorbik

> The both functions xcrb_msg_to_type6cprb_msgx() and
> xcrb_msg_to_type6_ep11cprb_msgx() copy the user space message into a
> kernel buffer based on the message length. But on further processing
> the message is supposed to be 4 byte length adjusted. Thus up to 3
> bytes of uninitialized kernel memory are forwarded to further
> processing steps and may unwanted expose kernel memory to the crypto
> card firmware.
> 
> This patch contains code to pad the gap between user space copied
> message and message buffer length sent down to further processing of
> the CCA or EP11 message to zeros.
> 
> Fixes: e2c6d91eb8b1 ("s390/zcrypt: Rework domain processing within zcrypt device driver")
> Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260730141509.205970-1-freude@linux.ibm.com?part=5


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 3/5] s390/zcrypt: Improve EP11 CPRB length and overflow checks
  2026-07-30 14:15 ` [PATCH v10 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
@ 2026-07-30 14:35   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-30 14:35 UTC (permalink / raw)
  To: Harald Freudenberger
  Cc: Heiko Carstens, linux-s390, Vasily Gorbik, Alexander Gordeev

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [Medium] The ASN.1 parser in `xcrb_msg_to_type6_ep11cprb_msgx()` can read one byte past the initialized user payload and branch on uninitialized memory if a maliciously crafted payload causes `offs` to exactly equal `pld_len`.

Pre-existing issues:
- [Critical] The patch replaces the vulnerable C struct overlay parsing in `xcrb_msg_to_type6_ep11cprb_msgx()` with a dynamic ASN.1 parser, but completely misses `zcrypt_msgtype6_send_ep11_cprb()`. The send function still uses the flawed, hardcoded `struct pld_hdr` overlay to dynamically patch the domain ID in the ASN.1 payload, allowing domain isolation bypass and data corruption.
- [Medium] Up to 3 bytes of uninitialized kernel heap memory can be transmitted to the hardware coprocessor due to unzeroed alignment padding.
--

--- Patch [3]: [PATCH v10 3/5] s390/zcrypt: Improve EP11 CPRB length and overflow checks ---
commit 92cbe0a3862e6fcbf60a93a6091ac8e0146860be
Author: Harald Freudenberger <freude@linux.ibm.com>

s390/zcrypt: Improve EP11 CPRB length and overflow checks

The xcrb_msg_to_type6_ep11cprb_msgx() function lacks proper input
validation, creating security vulnerabilities: missing minimum size
validation, arithmetic overflow in length calculations, and simple
c struct overlay for asn1 payload parsing. Fix by using size_t for
length calculations, adding boundary checks, and doing a very simple
asn1 parsing of the payload up to the function value field.

Cc: stable@vger.kernel.org # 7.1+

> diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
> --- a/drivers/s390/crypto/zcrypt_msgtype6.c
> +++ b/drivers/s390/crypto/zcrypt_msgtype6.c

[ ... ]

> @@ -492,26 +541,51 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap
>  
>  	/* Import CPRB data from the ioctl input parameter */
>  	if (z_copy_from_user(userspace, msg->userdata,
> -			     (char __force __user *)xcrb->req, xcrb->req_len)) {
> +			     (char __force __user *)xcrb->req, xcrb->req_len))
>  		return -EFAULT;
> -	}

[Severity: Medium]
This isn't a bug introduced by this patch, but does this code leave the
CEIL4 alignment padding uninitialized?

When xcrb->req_len is not a multiple of 4, the calculated ap_msg->len
includes the CEIL4 padding, but z_copy_from_user() only copies the exact
requested bytes without zeroing the trailing bytes. Could this leak up to
3 bytes of uninitialized kernel heap memory to the hardware coprocessor?

[ ... ]

> +	/* payload length field */
> +	i = asn1_length_decode(pld + offs, pld_len - offs, &u);
> +	if (i < 0)
> +		return -EINVAL;
> +	offs += i;
> +	if (offs > pld_len || u > pld_len - offs)
> +		return -EINVAL;

[Severity: Medium]
Does this bounds check allow an out-of-bounds read?

If a maliciously crafted payload has tag and length fields that consume
exactly pld_len bytes, the check offs > pld_len evaluates to false (for
example, when both are 6).

The parser then proceeds to the next line:

> +	/* function tag */
> +	if (pld[offs++] != 0x04)

This would read past the end of the user-provided payload buffer. Should
this check ensure there is at least 1 byte remaining before reading the
function tag?

> +		return -EINVAL;
> +	/* function length */
> +	if (offs > pld_len)
> +		return -EINVAL;

[ ... ]

[Severity: Critical]
This is a pre-existing issue, but does zcrypt_msgtype6_send_ep11_cprb()
also need to be updated to use the new ASN.1 parser instead of the C struct
overlay?

Looking at zcrypt_msgtype6_send_ep11_cprb() in this file, which was not
modified by the patch:

    payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt);
    payload_hdr->dom_val = AP_QID_QUEUE(zq->queue->qid);

If a user supplies a valid ASN.1 payload with a 1-byte or 2-byte function
value length, the kernel will patch the domain ID at a fixed offset since
it expects a 4-byte function value.

Could this overwrite the wrong bytes and leave the user-supplied domain ID
intact, potentially bypassing domain isolation when the hardware coprocessor
processes the request?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260730141509.205970-1-freude@linux.ibm.com?part=3

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
  2026-07-30 14:29   ` sashiko-bot
@ 2026-07-30 14:43     ` Harald Freudenberger
  0 siblings, 0 replies; 12+ messages in thread
From: Harald Freudenberger @ 2026-07-30 14:43 UTC (permalink / raw)
  To: sashiko-reviews
  Cc: Heiko Carstens, Vasily Gorbik, linux-s390, Alexander Gordeev

On 2026-07-30 16:29, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider:
> - [High] An off-by-one error in bounds checking allows a 1-byte
> out-of-bounds read when parsing the ASN.1 function tag.
> --
> 
> --- Patch [4]: [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain
> handling with ASN.1 parsing ---
> commit 195849014f55379e7fe6dcd2f7156dd9744b4047
> Author: Harald Freudenberger <freude@linux.ibm.com>
> 
> s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
> 
> This commit replaces struct overlays in EP11 CPRB domain handling with
> explicit ASN.1 parsing to improve security and maintainability. It adds
> bounds checking and a new helper function for safe integer encoding.
> 
>> diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c 
>> b/drivers/s390/crypto/zcrypt_msgtype6.c
>> --- a/drivers/s390/crypto/zcrypt_msgtype6.c
>> +++ b/drivers/s390/crypto/zcrypt_msgtype6.c
> 
> [ ... ]
> 
>> @@ -1239,51 +1261,94 @@ static long 
>> zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue *
> 
> [ ... ]
> 
>> +		/* payload tag */
>> +		if (pld[offs++] != 0x30)
>> +			return -EINVAL;
>> +		/* payload length field */
>> +		i = asn1_length_decode(pld + offs, pld_len - offs, &u);
>> +		if (i < 0)
>> +			return -EINVAL;
>> +		offs += i;
>> +		if (offs > pld_len || u > pld_len - offs)
>> +			return -EINVAL;
> 
> [Severity: High]
> Does this allow a 1-byte out-of-bounds read of the pld buffer if the 
> ASN.1
> payload length field uses a multi-byte encoding that exactly consumes 
> the
> remaining pld_len bytes?
> 
> If offs exactly equals pld_len, the bounds check offs > pld_len will 
> evaluate
> to false. If u is 0, u > pld_len - offs becomes 0 > 0, which is also 
> false.
> 
>> +		/* function tag */
>> +		if (pld[offs++] != 0x04)
> 
> The code would then proceed to read pld[offs++], reading one byte past 
> the
> end of the pld buffer.
> 
> Should this bounds check use >= instead?
> 
> if (offs >= pld_len || u > pld_len - offs)

This sounds sane to me. However, will deep check this. But ...why the 
hell tells
me Bob that all my code is good and secure and does not find any 
misaligned memory
access possibilities !?!?

> 
> Additionally, looking at xcrb_msg_to_type6_ep11cprb_msgx() in the same 
> file:
> 
> 	/* payload length field */
> 	i = asn1_length_decode(pld + offs, pld_len - offs, &u);
> 	if (i < 0)
> 		return -EINVAL;
> 	offs += i;
> 	if (offs > pld_len || u > pld_len - offs)
> 		return -EINVAL;
> 	/* function tag */
> 	if (pld[offs++] != 0x04)
> 
> This is a pre-existing issue, but does it suffer from the identical 
> bounds
> checking flaw?
> 
>> +			return -EINVAL;

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks
  2026-07-30 14:15 ` [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
@ 2026-07-30 14:48   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-30 14:48 UTC (permalink / raw)
  To: Harald Freudenberger
  Cc: Heiko Carstens, Vasily Gorbik, Alexander Gordeev, linux-s390

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [Medium] Uninitialized alignment padding bytes may be read during function code extraction in `xcrb_msg_to_type6cprb_msgx`.

Pre-existing issues:
- [Critical] A 64-bit integer overflow in `xcrb_msg_to_type6_ep11cprb_msgx` allows a massive `req_len` to bypass the buffer size check.
- [High] The EP11 request path lacks minimum size validation, leading to uninitialized kernel memory access.
- [High] A user can bypass administrative domain permission checks by setting the target domain to AUTOSEL_DOM (0xFFFF).
--

--- Patch [2]: [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks ---
commit 168ea93b9d93d40b4d0625f5a9d932544117496a
Author: Harald Freudenberger <freude@linux.ibm.com>

s390/zcrypt: Improve CCA CPRB length and overflow checks
    
This patch improves input validation in the xcrb_msg_to_type6cprb_msgx
function. It uses size_t for length calculations, adds bounds checks after
alignment, validates the minimum control block size, and detects overflow
in sum calculations.

> diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
> --- a/drivers/s390/crypto/zcrypt_msgtype6.c
> +++ b/drivers/s390/crypto/zcrypt_msgtype6.c

[ ... ]

> @@ -404,8 +395,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))

[Severity: Medium]
Can this relaxed bounds check result in reading uninitialized alignment
padding?

If a userspace caller sends a CCA ioctl where
xcrb->request_control_blk_length is not a multiple of 4 (for example, 21),
the z_copy_from_user() call copies exactly 21 bytes. Up to 3 bytes of
padding might be left uninitialized.

Because the check now compares against req_cblen instead of
xcrb->request_control_blk_length, could a crafted cprbx.cprb_len (e.g., 22)
pass this validation and point into the uninitialized padding area, allowing
the subsequent memcpy() to read uninitialized kernel heap memory into
msg->hdr.function_code?

>  		return -EINVAL;
>  	function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
>  	memcpy(msg->hdr.function_code, function_code,

[ ... ]


While reviewing these length check improvements, I also noticed a few
pre-existing issues in related code:


[Severity: Critical]
This isn't a bug introduced by this patch, but does
xcrb_msg_to_type6_ep11cprb_msgx() suffer from a similar 64-bit integer
overflow?

In drivers/s390/crypto/zcrypt_msgtype6.c:xcrb_msg_to_type6_ep11cprb_msgx():

    ap_msg->len = sizeof(struct type6_hdr) + CEIL4(xcrb->req_len);
    if (ap_msg->len > ap_msg->bufsize)
        return -EINVAL;

If a userspace caller passes an ioctl with req_len equal to U64_MAX - 79,
CEIL4(req_len) evaluates to U64_MAX - 79. Adding sizeof(struct type6_hdr)
wraps the 64-bit value to 8, completely bypassing the buffer bounds check.

Would the subsequent copy_from_user() then zero-fill the kernel heap on
failure and cause memory corruption?


[Severity: High]
This is a pre-existing issue, but the EP11 request path seems to lack the
minimum size validation that this patch adds to the CCA path.

In drivers/s390/crypto/zcrypt_msgtype6.c:xcrb_msg_to_type6_ep11cprb_msgx():

    if (z_copy_from_user(userspace, msg->userdata,
                         (char __force __user *)xcrb->req, xcrb->req_len)) {

If an attacker submits an EP11 ioctl with a small req_len (e.g., 4 bytes),
the kernel copies only 4 bytes. Could the function then read EP11 structure
fields such as msg->pld_lenfmt and msg->cprbx.flags from uninitialized
memory to determine function codes and domains?


[Severity: High]
This isn't a bug introduced by this patch, but looking at the domain
permissions, is there a way to bypass administrative checks?

In drivers/s390/crypto/zcrypt_api.c:_zcrypt_send_cprb():

    if (perms != &ap_perms && domain < AP_DOMAINS) {
        if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
            if (!test_bit_inv(domain, perms->adm)) {

And similarly in _zcrypt_send_ep11_cprb():

    if (perms != &ap_perms && domain < AUTOSEL_DOM) {
        if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
            if (!test_bit_inv(domain, perms->adm)) {

If a user submits a CPRBX or EP11 request containing an administrative
command and sets the embedded domain field to 0xFFFF (AUTOSEL_DOM), the
condition domain < AP_DOMAINS evaluates to false.

Does this cause the administrative permission check to be completely skipped,
allowing a user with only usage privileges to execute administrative
commands on that domain?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260730141509.205970-1-freude@linux.ibm.com?part=2

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-07-30 14:48 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 14:15 [PATCH v10 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
2026-07-30 14:15 ` [PATCH v10 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
2026-07-30 14:25   ` sashiko-bot
2026-07-30 14:15 ` [PATCH v10 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
2026-07-30 14:48   ` sashiko-bot
2026-07-30 14:15 ` [PATCH v10 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
2026-07-30 14:35   ` sashiko-bot
2026-07-30 14:15 ` [PATCH v10 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
2026-07-30 14:29   ` sashiko-bot
2026-07-30 14:43     ` Harald Freudenberger
2026-07-30 14:15 ` [PATCH v10 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
2026-07-30 14:29   ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.