* [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling
@ 2026-07-29 15:44 Harald Freudenberger
2026-07-29 15:44 ` [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Harald Freudenberger @ 2026-07-29 15:44 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.
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 | 358 ++++++++++++++++++--------
3 files changed, 271 insertions(+), 123 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
2026-07-29 15:44 [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
@ 2026-07-29 15:44 ` Harald Freudenberger
2026-07-29 16:02 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Harald Freudenberger @ 2026-07-29 15:44 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.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
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] 11+ messages in thread
* [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks
2026-07-29 15:44 [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
2026-07-29 15:44 ` [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
@ 2026-07-29 15:44 ` Harald Freudenberger
2026-07-29 16:19 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Harald Freudenberger @ 2026-07-29 15:44 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.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
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] 11+ messages in thread
* [PATCH v9 3/5] s390/zcrypt: Improve EP11 CPRB length and overflow checks
2026-07-29 15:44 [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
2026-07-29 15:44 ` [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
2026-07-29 15:44 ` [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
@ 2026-07-29 15:44 ` Harald Freudenberger
2026-07-29 16:35 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
2026-07-29 15:44 ` [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
4 siblings, 1 reply; 11+ messages in thread
From: Harald Freudenberger @ 2026-07-29 15:44 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.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
drivers/s390/crypto/zcrypt_msgtype6.c | 148 +++++++++++++++++++-------
1 file changed, 109 insertions(+), 39 deletions(-)
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index fb37e28c8242..c4b5d9b85f7a 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -439,12 +439,61 @@ 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)(*((u8 *)buf));
+ return 1;
+ case 2:
+ *u = (u32)be16_to_cpup((u16 *)buf);
+ return 2;
+ case 4:
+ *u = (u32)be32_to_cpup((u32 *)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 const size_t min_pld_size = 5 * sizeof(unsigned char);
+
static struct type6_hdr static_type6_ep11_hdr = {
.type = 0x06,
.rqid = {0x00, 0x01},
@@ -456,34 +505,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_pld_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 +539,49 @@ 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_pld_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 (u > pld_len - offs)
+ return -EINVAL;
+ /* function tag */
+ if (pld[offs++] != 0x04)
+ return -EINVAL;
+ /* function length */
+ i = asn1_length_decode(pld + offs, pld_len - offs, &u);
+ if (i < 0)
+ return -EINVAL;
+ offs += i;
+ if (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] 11+ messages in thread
* [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
2026-07-29 15:44 [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
` (2 preceding siblings ...)
2026-07-29 15:44 ` [PATCH v9 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
@ 2026-07-29 15:44 ` Harald Freudenberger
2026-07-29 16:46 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
4 siblings, 1 reply; 11+ messages in thread
From: Harald Freudenberger @ 2026-07-29 15:44 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.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
drivers/s390/crypto/zcrypt_msgtype6.c | 124 +++++++++++++++++++-------
1 file changed, 93 insertions(+), 31 deletions(-)
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
index c4b5d9b85f7a..da7580933c6f 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"
@@ -1223,6 +1224,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.
@@ -1235,51 +1258,90 @@ 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;
+
+ /*
+ * 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 (u > pld_len - offs)
+ return -EINVAL;
+ /* function tag */
+ if (pld[offs++] != 0x04)
+ return -EINVAL;
+ /* function length */
+ 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 (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 (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] 11+ messages in thread
* [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros
2026-07-29 15:44 [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
` (3 preceding siblings ...)
2026-07-29 15:44 ` [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
@ 2026-07-29 15:44 ` Harald Freudenberger
2026-07-29 17:07 ` sashiko-bot
4 siblings, 1 reply; 11+ messages in thread
From: Harald Freudenberger @ 2026-07-29 15:44 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.
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
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 da7580933c6f..4a60540b0700 100644
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -396,6 +396,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;
@@ -542,6 +546,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] 11+ messages in thread
* Re: [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
2026-07-29 15:44 ` [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
@ 2026-07-29 16:02 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29 16:02 UTC (permalink / raw)
To: Harald Freudenberger
Cc: linux-s390, Alexander Gordeev, Heiko Carstens, Vasily Gorbik
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Uninitialized kernel heap memory padding is leaked to the crypto hardware via the AP bus message buffer.
--
--- Patch [1]: [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code ---
commit bbdecb3add414a50e3e90e0021b88d5f9c7bf242
Author: Harald Freudenberger <freude@linux.ibm.com>
s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
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.
> diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
> index 84936a795b956..6819acd67c49b 100644
> --- a/drivers/s390/crypto/zcrypt_ccamisc.c
> +++ b/drivers/s390/crypto/zcrypt_ccamisc.c
[ ... ]
[Severity: High]
This isn't a bug introduced by this patch, but while reviewing how CPRB
requests are padded and aligned, I noticed a pre-existing issue in
drivers/s390/crypto/zcrypt_msgtype6.c.
drivers/s390/crypto/zcrypt_msgtype6.c:xcrb_msg_to_type6cprb_msgx() {
...
/* prepare CPRB */
if (z_copy_from_user(userspace, msg->userdata,
xcrb->request_control_blk_addr,
xcrb->request_control_blk_length))
return -EFAULT;
...
}
Does this code leak uninitialized kernel heap memory padding to the crypto
hardware via the AP bus message buffer?
When a userspace application calls the zcrypt ioctl (e.g., ICARSAMODEXPO)
with an unaligned request_control_blk_length, the ap_msg->msg buffer is
allocated via kmalloc without __GFP_ZERO by ap_init_apmsg().
Since only the unaligned length is copied into the buffer, while the total
message length sent to the AP card uses the 4-byte aligned CEIL4() length,
could the unwritten padding bytes (up to 3 bytes) retain their previous
kernel heap contents and be transmitted to the hardware?
[Severity: High]
This is a pre-existing issue, but the same behavior appears in
xcrb_msg_to_type6_ep11cprb_msgx():
drivers/s390/crypto/zcrypt_msgtype6.c:xcrb_msg_to_type6_ep11cprb_msgx() {
...
/* Import CPRB data from the ioctl input parameter */
if (z_copy_from_user(userspace, msg->userdata,
(char __force __user *)xcrb->req, xcrb->req_len)) {
return -EFAULT;
}
...
}
Can the unaligned request length copied here also leave padding bytes
uninitialized before being sent to the hardware?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729154458.195135-1-freude@linux.ibm.com?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks
2026-07-29 15:44 ` [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
@ 2026-07-29 16:19 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29 16:19 UTC (permalink / raw)
To: Harald Freudenberger
Cc: linux-s390, Vasily Gorbik, Heiko Carstens, Alexander Gordeev
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Uninitialized memory read due to incorrect bounds check when copying subfunction code.
- [Low] Redundant and mathematically impossible integer overflow checks on 64-bit sum calculations.
Pre-existing issues:
- [High] Missing minimum size validation for EP11 CPRB messages allows reading uninitialized memory.
--
--- Patch [2]: [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks ---
commit 93cf9dfadb28a27fc37f0252a7721661ccec467d
Author: Harald Freudenberger <freude@linux.ibm.com>
s390/zcrypt: Improve CCA CPRB length and overflow checks
This commit addresses integer overflow and missing bounds checks in the
xcrb_msg_to_type6cprb_msgx function during CCA CPRB processing. It introduces
64-bit size_t variables for length arithmetic and explicitly bounds the
calculated lengths to U32_MAX. Minimum control block sizes are also validated
prior to copying data from userspace.
> --- 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,
> + 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;
> }
[Severity: Low]
Does this ternary check provide any value now that the lengths are using
64-bit size_t?
Since req_cblen and req_sumlen are bounded to U32_MAX, their sum cannot
overflow a 64-bit variable, making this condition impossible to reach.
[ ... ]
> @@ -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,
[Severity: High]
Can this modification lead to an uninitialized memory read?
If an attacker passes a CPRB request where xcrb->request_control_blk_length
is 13 bytes, the aligned req_cblen becomes 16. If they also set
cprbx.cprb_len to 14, z_copy_from_user() will copy only 13 bytes, leaving
the padding uninitialized.
The new bounds check allows cprb_len = 14 (since 14 is not greater than
16 - 2), and the code then copies 2 uninitialized bytes starting at offset 14
into msg->hdr.function_code. This could leak kernel heap memory to the
crypto device and influence routing decisions.
[Severity: High]
This isn't a bug introduced by this patch, but I noticed a similar missing
minimum size validation in xcrb_msg_to_type6_ep11cprb_msgx().
drivers/s390/crypto/zcrypt_msgtype6.c:xcrb_msg_to_type6_ep11cprb_msgx() {
...
/* Import CPRB data from the ioctl input parameter */
if (z_copy_from_user(userspace, msg->userdata,
(char __force __user *)xcrb->req, xcrb->req_len)) {
return -EFAULT;
}
if ((msg->pld_lenfmt & 0x80) == 0x80) {
...
}
Does this function also need a minimum size validation?
If a user issues the ZSENDEP11CPRB ioctl with an EP11 user request block
where req_len is extremely small (e.g., 0), z_copy_from_user() will succeed
by copying 0 bytes. The kernel then evaluates fields like msg->pld_lenfmt,
msg->cprbx.flags, and msg->cprbx.target_id from uninitialized kernel heap
memory, potentially allowing bypasses of authorization boundaries.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729154458.195135-1-freude@linux.ibm.com?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v9 3/5] s390/zcrypt: Improve EP11 CPRB length and overflow checks
2026-07-29 15:44 ` [PATCH v9 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
@ 2026-07-29 16:35 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29 16:35 UTC (permalink / raw)
To: Harald Freudenberger
Cc: Vasily Gorbik, Alexander Gordeev, Heiko Carstens, linux-s390
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The `asn1_int_decode` function casts potentially unaligned byte pointers to `u16 *` and `u32 *`, invoking undefined behavior.
Pre-existing issues:
- [Critical] The patch updates `xcrb_msg_to_type6_ep11cprb_msgx()` to flexibly parse ASN.1 payloads but leaves the legacy fixed C-struct overlay (`struct pld_hdr`) in `zcrypt_msgtype6_send_ep11_cprb()` untouched, allowing domain isolation to be bypassed.
--
--- Patch [3]: [PATCH v9 3/5] s390/zcrypt: Improve EP11 CPRB length and overflow checks ---
commit ea4312469ba49e7cc43bf68e3a1d383e305d99b5
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:
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.
> diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
> index fb37e28c82424..c4b5d9b85f7a9 100644
> --- a/drivers/s390/crypto/zcrypt_msgtype6.c
> +++ b/drivers/s390/crypto/zcrypt_msgtype6.c
> @@ -439,12 +439,61 @@ 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)(*((u8 *)buf));
> + return 1;
> + case 2:
> + *u = (u32)be16_to_cpup((u16 *)buf);
[Severity: High]
Does this code trigger undefined behavior?
The buf parameter points to an arbitrary offset within an ASN.1 encoded
payload in xcrb_msg_to_type6_ep11cprb_msgx(), which is not guaranteed to
be 2-byte aligned. Casting this unaligned u8 * to u16 * and passing it to
be16_to_cpup() violates C strict alignment rules.
While s390 hardware generally supports scalar unaligned accesses, the
compiler may assume u16 * is properly aligned and emit instructions that
strictly require alignment, potentially causing a kernel panic when parsing
untrusted payloads.
> + return 2;
> + case 4:
> + *u = (u32)be32_to_cpup((u32 *)buf);
[Severity: High]
Does this code also trigger undefined behavior for 4-byte values?
Similar to the 2-byte case above, this casts a potentially unaligned byte
pointer to a u32 *, which could cause an Oops if the compiler emits
alignment-strict instructions.
> + return 4;
> + default:
> + return -EINVAL;
> + }
> +}
[ ... ]
> - 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;
> -
[Severity: Critical]
This is a pre-existing issue, but this patch updates
xcrb_msg_to_type6_ep11cprb_msgx() to parse ASN.1 payloads dynamically,
while leaving the legacy fixed C-struct overlay (struct pld_hdr) untouched
in zcrypt_msgtype6_send_ep11_cprb().
Does this allow the cryptographic domain isolation to be bypassed?
The kernel enforces crypto domain isolation by overwriting the dom_val field
in the user-provided EP11 payload with the queue's domain:
drivers/s390/crypto/zcrypt_msgtype6.c:zcrypt_msgtype6_send_ep11_cprb() {
...
payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt);
payload_hdr->dom_val = AP_QID_QUEUE(zq->queue->qid);
...
}
Because this still uses the hardcoded C-struct which assumes func_val is
exactly 4 bytes, what happens if an unprivileged user crafts an ASN.1
payload where the function ID is encoded in 1 or 2 bytes?
It appears the actual dom_val would be shifted earlier in the payload, and
the kernel would overwrite the wrong bytes at a fixed offset, leaving the
user-controlled domain ID unmodified. This seems to allow an attacker to
execute crypto commands against unauthorized domains.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729154458.195135-1-freude@linux.ibm.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
2026-07-29 15:44 ` [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
@ 2026-07-29 16:46 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29 16:46 UTC (permalink / raw)
To: Harald Freudenberger
Cc: Heiko Carstens, linux-s390, Vasily Gorbik, Alexander Gordeev
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Missing bounds checking before reading `pld[offs++]` causes an integer underflow in `pld_len - offs`, bypassing bounds checks and enabling an Out-Of-Bounds (OOB) read and write.
--
--- Patch [4]: [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing ---
commit 750aceaeb85ad5fa9ff8e2614110f89a4c147522
Author: Harald Freudenberger <freude@linux.ibm.com>
s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing
This commit replaces struct overlays with explicit ASN.1 parsing for EP11
CPRB domain handling. It introduces bounds checking at each parsing step
and adds a helper function to safely write integer values.
> 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
[ ... ]
> @@ -1235,51 +1258,90 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue *
> struct ap_message *ap_msg)
> {
[ ... ]
> + /* payload length field */
> + i = asn1_length_decode(pld + offs, pld_len - offs, &u);
> + if (i < 0)
> + return -EINVAL;
> + offs += i;
> + if (u > pld_len - offs)
> + return -EINVAL;
> + /* function tag */
> + if (pld[offs++] != 0x04)
[Severity: Critical]
If the ASN.1 payload tag and length encoding completely consume the payload
buffer, couldn't offs become equal to pld_len here?
If offs equals pld_len, does this array access read out of bounds and
increment offs past pld_len?
If offs exceeds pld_len, it appears the subsequent pld_len - offs
calculations will underflow to SIZE_MAX. Would this bypass the remaining
bounds checks, such as pld_len - offs >= 3, and potentially allow
asn1_int_encode() to write out of bounds?
Additionally, it looks like a similar pattern exists in
xcrb_msg_to_type6_ep11cprb_msgx() where pld[offs++] is checked for the
function tag without a prior bounds check.
> + return -EINVAL;
> + /* function length */
> + i = asn1_length_decode(pld + offs, pld_len - offs, &u);
> + if (i < 0)
> + return -EINVAL;
> + offs += i;
> + if (u > pld_len - offs)
> + return -EINVAL;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729154458.195135-1-freude@linux.ibm.com?part=4
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros
2026-07-29 15:44 ` [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
@ 2026-07-29 17:07 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-29 17:07 UTC (permalink / raw)
To: Harald Freudenberger
Cc: linux-s390, Alexander Gordeev, Vasily Gorbik, Heiko Carstens
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but an integer overflow and incomplete bounds checking in hardware response parsing allows a massive out-of-bounds kernel memory leak to userspace.
--
--- Patch [5]: [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros ---
commit 0141bfa352999fc87f7b7c58c7c727f991c4b416
Author: Harald Freudenberger <freude@linux.ibm.com>
s390/zcrypt: Pad trailing CCA or EP11 message with zeros
This patch fixes an information leak to the crypto card firmware. It zeroes
out the gap between the user space copied message and the message buffer
length sent to the CCA or EP11 message processing.
> diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
> index da7580933c6f2..4a60540b07003 100644
> --- a/drivers/s390/crypto/zcrypt_msgtype6.c
> +++ b/drivers/s390/crypto/zcrypt_msgtype6.c
[ ... ]
[Severity: High]
This is a pre-existing issue, but I noticed an integer overflow and
incomplete bounds checking in hardware response parsing that could allow an
out-of-bounds kernel memory leak to userspace.
In zcrypt_msgtype6_receive():
if (t86r->fmt2.count2)
len = t86r->fmt2.offset2 + t86r->fmt2.count2;
else
len = t86r->fmt2.offset1 + t86r->fmt2.count1;
If a compromised or malicious crypto device firmware returns a response where
offset1 + count1 overflows 32-bit arithmetic, it might bypass the buffer
size check. Alternatively, if count2 is non-zero, the bounds check in
zcrypt_msgtype6_receive() only validates offset2 + count2 and completely
ignores offset1 + count1.
Later in convert_type86_xcrb():
if (z_copy_to_user(userspace, xcrb->reply_control_blk_addr,
data + msg->fmt2.offset1, msg->fmt2.count1))
return -EFAULT;
Since the bounds check can pass under those conditions, could offset1 be
used as an unvalidated offset into kernel memory here, resulting in
z_copy_to_user() copying out-of-bounds kernel memory into the user-provided
buffer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260729154458.195135-1-freude@linux.ibm.com?part=5
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-07-29 17:07 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 15:44 [PATCH v9 0/5] Improve code in zcrypt msg type 6 handling Harald Freudenberger
2026-07-29 15:44 ` [PATCH v9 1/5] s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code Harald Freudenberger
2026-07-29 16:02 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 2/5] s390/zcrypt: Improve CCA CPRB length and overflow checks Harald Freudenberger
2026-07-29 16:19 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 3/5] s390/zcrypt: Improve EP11 " Harald Freudenberger
2026-07-29 16:35 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 4/5] s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing Harald Freudenberger
2026-07-29 16:46 ` sashiko-bot
2026-07-29 15:44 ` [PATCH v9 5/5] s390/zcrypt: Pad trailing CCA or EP11 message with zeros Harald Freudenberger
2026-07-29 17:07 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox