mirror of
https://github.com/torvalds/linux.git
synced 2026-09-19 17:18:02 +02:00
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.
Fixes: e2c6d91eb8 ("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+
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
This commit is contained in:
parent
35ac6754ba
commit
17ac0bc866
|
|
@ -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");
|
||||
|
|
@ -438,12 +442,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},
|
||||
|
|
@ -455,34 +506,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;
|
||||
size_t req_len, rep_len, pld_len;
|
||||
unsigned char *pld;
|
||||
int offs = 0, i;
|
||||
unsigned int u;
|
||||
|
||||
if (CEIL4(xcrb->req_len) < xcrb->req_len)
|
||||
return -EINVAL; /* overflow after alignment*/
|
||||
|
||||
/* 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;
|
||||
|
|
@ -491,26 +540,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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user