Blob Blame History Raw
From: Harald Freudenberger <freude@linux.ibm.com>
Date: Tue, 29 Sep 2020 16:07:22 +0200
Subject: s390/zcrypt: Introduce Failure Injection feature
Git-commit: 27c4f6738bdc535e42dfc1295dadc78ab7582939
Patch-mainline: v5.10-rc1
References: jsc#SLE-13815 bsc#1178402 LTC#186349

Introduce a way to specify additional debug flags with an crpyto
request to be able to trigger certain failures within the zcrypt
device drivers and/or ap core code.

This failure injection possibility is only enabled with a kernel debug
build CONFIG_ZCRYPT_DEBUG) and should never be available on a regular
kernel running in production environment.

Details:

* The ioctl(ICARSAMODEXPO) get's a struct ica_rsa_modexpo. If the
  leftmost bit of the 32 bit unsigned int inputdatalength field is
  set, the uppermost 16 bits are separated and used as debug flag
  value. The process is checked to have the CAP_SYS_ADMIN capability
  enabled or EPERM is returned.

* The ioctl(ICARSACRT) get's a struct ica_rsa_modexpo_crt. If the
  leftmost bit of the 32 bit unsigned int inputdatalength field is set,
  the uppermost 16 bits are separated and used als debug flag
  value. The process is checked to have the CAP_SYS_ADMIN capability
  enabled or EPERM is returned.

* The ioctl(ZSECSENDCPRB) used to send CCA CPRBs get's a struct
  ica_xcRB. If the leftmost bit of the 32 bit unsigned int status
  field is set, the uppermost 16 bits of this field are used as debug
  flag value. The process is checked to have the CAP_SYS_ADMIN
  capability enabled or EPERM is returned.

* The ioctl(ZSENDEP11CPRB) used to send EP11 CPRBs get's a struct
  ep11_urb. If the leftmost bit of the 64 bit unsigned int req_len
  field is set, the uppermost 16 bits of this field are used as debug
  flag value. The process is checked to have the CAP_SYS_ADMIN
  capability enabled or EPERM is returned.

So it is possible to send an additional 16 bit value to the zcrypt API
to be used to carry a failure injection command which may trigger
special behavior within the zcrypt API and layers below. This 16 bit
value is for the rest of the test referred as 'fi command' for Failure
Injection.

The lower 8 bits of the fi command construct a numerical argument in
the range of 1-255 and is the 'fi action' to be performed with the
request or the resulting reply:

* 0x00 (all requests): No failure injection action but flags may be
  provided which may affect the processing of the request or reply.
* 0x01 (only CCA CPRBs): The CPRB's agent_ID field is set to
  'FF'. This results in an reply code 0x90 (Transport-Protocol
  Failure).
* 0x02 (only CCA CPRBs): After the APQN to send to has been chosen,
  the domain field within the CPRB is overwritten with value 99 to
  enforce an reply with RY 0x8A.
* 0x03 (all requests): At NQAP invocation the invalid qid value 0xFF00
  is used causing an response code of 0x01 (AP queue not valid).

The upper 8 bits of the fi command may carry bit flags which may
influence the processing of an request or response:

* 0x01: No retry. If this bit is set, the usual loop in the zcrypt API
  which retries an CPRB up to 10 times when the lower layers return
  with EAGAIN is abandoned after the first attempt to send the CPRB.
* 0x02: Toggle special. Toggles the special bit on this request. This
  should result in an reply code RY~0x41 and result in an ioctl
  failure with errno EINVAL.

This failure injection possibilities may get some further extensions
in the future. As of now this is a starting point for Continuous Test
and Integration to trigger some failures and watch for the reaction of
the ap bus and zcrypt device driver code.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/s390/crypto/ap_bus.h           |   29 +++++++++-
 drivers/s390/crypto/ap_queue.c         |   10 +++
 drivers/s390/crypto/zcrypt_api.c       |   91 +++++++++++++++++++++++++++++++++
 drivers/s390/crypto/zcrypt_api.h       |    3 +
 drivers/s390/crypto/zcrypt_msgtype50.c |   11 +++
 drivers/s390/crypto/zcrypt_msgtype6.c  |   10 +++
 6 files changed, 151 insertions(+), 3 deletions(-)

--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -203,12 +203,37 @@ struct ap_queue {
 
 typedef enum ap_sm_wait (ap_func_t)(struct ap_queue *queue);
 
+/* failure injection cmd struct */
+struct ap_fi {
+	union {
+		u16 cmd;		/* fi flags + action */
+		struct {
+			u8 flags;	/* fi flags only */
+			u8 action;	/* fi action only */
+		};
+	};
+};
+
+/* all currently known fi actions */
+enum ap_fi_actions {
+	AP_FI_ACTION_CCA_AGENT_FF   = 0x01,
+	AP_FI_ACTION_CCA_DOM_INVAL  = 0x02,
+	AP_FI_ACTION_NQAP_QID_INVAL = 0x03,
+};
+
+/* all currently known fi flags */
+enum ap_fi_flags {
+	AP_FI_FLAG_NO_RETRY	  = 0x01,
+	AP_FI_FLAG_TOGGLE_SPECIAL = 0x02,
+};
+
 struct ap_message {
 	struct list_head list;		/* Request queueing. */
 	unsigned long long psmid;	/* Message id. */
 	void *msg;			/* Pointer to message buffer. */
 	unsigned int len;		/* Message length. */
-	u32 flags;			/* Flags, see AP_MSG_FLAG_xxx */
+	u16 flags;			/* Flags, see AP_MSG_FLAG_xxx */
+	struct ap_fi fi;		/* Failure Injection cmd */
 	int rc;				/* Return code for this message */
 	void *private;			/* ap driver private pointer. */
 	/* receive is called from tasklet context */
@@ -216,7 +241,7 @@ struct ap_message {
 			struct ap_message *);
 };
 
-#define AP_MSG_FLAG_SPECIAL  (1 << 16)	/* flag msg as 'special' with NQAP */
+#define AP_MSG_FLAG_SPECIAL  1		/* flag msg as 'special' with NQAP */
 
 /**
  * ap_init_message() - Initialize ap_message.
--- a/drivers/s390/crypto/ap_queue.c
+++ b/drivers/s390/crypto/ap_queue.c
@@ -239,12 +239,20 @@ static enum ap_sm_wait ap_sm_write(struc
 {
 	struct ap_queue_status status;
 	struct ap_message *ap_msg;
+	ap_qid_t qid = aq->qid;
 
 	if (aq->requestq_count <= 0)
 		return AP_SM_WAIT_NONE;
 	/* Start the next request on the queue. */
 	ap_msg = list_entry(aq->requestq.next, struct ap_message, list);
-	status = __ap_send(aq->qid, ap_msg->psmid,
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (ap_msg->fi.action == AP_FI_ACTION_NQAP_QID_INVAL) {
+		AP_DBF_WARN("%s fi cmd 0x%04x: forcing invalid qid 0xFF00\n",
+			    __func__, ap_msg->fi.cmd);
+		qid = 0xFF00;
+	}
+#endif
+	status = __ap_send(qid, ap_msg->psmid,
 			   ap_msg->msg, ap_msg->len,
 			   ap_msg->flags & AP_MSG_FLAG_SPECIAL);
 	switch (status.response_code) {
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -25,6 +25,7 @@
 #include <linux/debugfs.h>
 #include <linux/cdev.h>
 #include <linux/ctype.h>
+#include <linux/capability.h>
 #include <asm/debug.h>
 
 #define CREATE_TRACE_POINTS
@@ -654,6 +655,11 @@ static long zcrypt_rsa_modexpo(struct ap
 
 	ap_init_message(&ap_msg);
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (tr && tr->fi.cmd)
+		ap_msg.fi.cmd = tr->fi.cmd;
+#endif
+
 	if (mex->outputdatalength < mex->inputdatalength) {
 		func_code = 0;
 		rc = -EINVAL;
@@ -757,6 +763,11 @@ static long zcrypt_rsa_crt(struct ap_per
 
 	ap_init_message(&ap_msg);
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (tr && tr->fi.cmd)
+		ap_msg.fi.cmd = tr->fi.cmd;
+#endif
+
 	if (crt->outputdatalength < crt->inputdatalength) {
 		func_code = 0;
 		rc = -EINVAL;
@@ -861,6 +872,17 @@ static long _zcrypt_send_cprb(bool users
 
 	xcRB->status = 0;
 	ap_init_message(&ap_msg);
+
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (tr && tr->fi.cmd)
+		ap_msg.fi.cmd = tr->fi.cmd;
+	if (tr && tr->fi.action == AP_FI_ACTION_CCA_AGENT_FF) {
+		ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid agent_ID 'FF'\n",
+				__func__, tr->fi.cmd);
+		xcRB->agent_ID = 0x4646;
+	}
+#endif
+
 	rc = get_cprb_fc(userspace, xcRB, &ap_msg, &func_code, &domain);
 	if (rc)
 		goto out;
@@ -936,6 +958,14 @@ static long _zcrypt_send_cprb(bool users
 	if (*domain == AUTOSEL_DOM)
 		*domain = AP_QID_QUEUE(qid);
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (tr && tr->fi.action == AP_FI_ACTION_CCA_DOM_INVAL) {
+		ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid domain\n",
+				__func__, tr->fi.cmd);
+		*domain = 99;
+	}
+#endif
+
 	rc = pref_zq->ops->send_cprb(userspace, pref_zq, xcRB, &ap_msg);
 
 	spin_lock(&zcrypt_list_lock);
@@ -1004,6 +1034,11 @@ static long _zcrypt_send_ep11_cprb(bool
 
 	ap_init_message(&ap_msg);
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (tr && tr->fi.cmd)
+		ap_msg.fi.cmd = tr->fi.cmd;
+#endif
+
 	target_num = (unsigned short) xcrb->targets_num;
 
 	/* empty list indicates autoselect (all available targets) */
@@ -1386,10 +1421,24 @@ static int icarsamodexpo_ioctl(struct ap
 	memset(&tr, 0, sizeof(tr));
 	if (copy_from_user(&mex, umex, sizeof(mex)))
 		return -EFAULT;
+
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (mex.inputdatalength & (1U << 31)) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		tr.fi.cmd = (u16)(mex.inputdatalength >> 16);
+	}
+	mex.inputdatalength &= 0x0000FFFF;
+#endif
+
 	do {
 		rc = zcrypt_rsa_modexpo(perms, &tr, &mex);
 		if (rc == -EAGAIN)
 			tr.again_counter++;
+#ifdef CONFIG_ZCRYPT_DEBUG
+		if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY))
+			break;
+#endif
 	} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
 	/* on failure: retry once again after a requested rescan */
 	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
@@ -1415,10 +1464,24 @@ static int icarsacrt_ioctl(struct ap_per
 	memset(&tr, 0, sizeof(tr));
 	if (copy_from_user(&crt, ucrt, sizeof(crt)))
 		return -EFAULT;
+
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (crt.inputdatalength & (1U << 31)) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		tr.fi.cmd = (u16)(crt.inputdatalength >> 16);
+	}
+	crt.inputdatalength &= 0x0000FFFF;
+#endif
+
 	do {
 		rc = zcrypt_rsa_crt(perms, &tr, &crt);
 		if (rc == -EAGAIN)
 			tr.again_counter++;
+#ifdef CONFIG_ZCRYPT_DEBUG
+		if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY))
+			break;
+#endif
 	} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
 	/* on failure: retry once again after a requested rescan */
 	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
@@ -1444,10 +1507,24 @@ static int zsecsendcprb_ioctl(struct ap_
 	memset(&tr, 0, sizeof(tr));
 	if (copy_from_user(&xcRB, uxcRB, sizeof(xcRB)))
 		return -EFAULT;
+
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (xcRB.status & (1U << 31)) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		tr.fi.cmd = (u16)(xcRB.status >> 16);
+	}
+	xcRB.status &= 0x0000FFFF;
+#endif
+
 	do {
 		rc = _zcrypt_send_cprb(true, perms, &tr, &xcRB);
 		if (rc == -EAGAIN)
 			tr.again_counter++;
+#ifdef CONFIG_ZCRYPT_DEBUG
+		if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY))
+			break;
+#endif
 	} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
 	/* on failure: retry once again after a requested rescan */
 	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
@@ -1474,10 +1551,24 @@ static int zsendep11cprb_ioctl(struct ap
 	memset(&tr, 0, sizeof(tr));
 	if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb)))
 		return -EFAULT;
+
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (xcrb.req_len & (1ULL << 63)) {
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		tr.fi.cmd = (u16)(xcrb.req_len >> 48);
+	}
+	xcrb.req_len &= 0x0000FFFFFFFFFFFFULL;
+#endif
+
 	do {
 		rc = _zcrypt_send_ep11_cprb(true, perms, &tr, &xcrb);
 		if (rc == -EAGAIN)
 			tr.again_counter++;
+#ifdef CONFIG_ZCRYPT_DEBUG
+		if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY))
+			break;
+#endif
 	} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
 	/* on failure: retry once again after a requested rescan */
 	if ((rc == -ENODEV) && (zcrypt_process_rescan()))
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -60,6 +60,9 @@ struct zcrypt_track {
 	int again_counter;		/* retry attempts counter */
 	int last_qid;			/* last qid used */
 	int last_rc;			/* last return code */
+#ifdef CONFIG_ZCRYPT_DEBUG
+	struct ap_fi fi;		/* failure injection cmd */
+#endif
 };
 
 /* defines related to message tracking */
--- a/drivers/s390/crypto/zcrypt_msgtype50.c
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -246,6 +246,12 @@ static int ICAMEX_msg_to_type50MEX_msg(s
 	    copy_from_user(exp, mex->b_key, mod_len) ||
 	    copy_from_user(inp, mex->inputdata, mod_len))
 		return -EFAULT;
+
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL)
+		ap_msg->flags ^= AP_MSG_FLAG_SPECIAL;
+#endif
+
 	return 0;
 }
 
@@ -332,6 +338,11 @@ static int ICACRT_msg_to_type50CRT_msg(s
 	    copy_from_user(inp, crt->inputdata, mod_len))
 		return -EFAULT;
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL)
+		ap_msg->flags ^= AP_MSG_FLAG_SPECIAL;
+#endif
+
 	return 0;
 }
 
--- a/drivers/s390/crypto/zcrypt_msgtype6.c
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -482,6 +482,11 @@ static int XCRB_msg_to_type6CPRB_msgX(bo
 	    || memcmp(function_code, "AU", 2) == 0)
 		ap_msg->flags |= AP_MSG_FLAG_SPECIAL;
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL)
+		ap_msg->flags ^= AP_MSG_FLAG_SPECIAL;
+#endif
+
 	/* copy data block */
 	if (xcRB->request_data_length &&
 	    z_copy_from_user(userspace, req_data, xcRB->request_data_address,
@@ -569,6 +574,11 @@ static int xcrb_msg_to_type6_ep11cprb_ms
 	if (msg->cprbx.flags & 0x20)
 		ap_msg->flags |= AP_MSG_FLAG_SPECIAL;
 
+#ifdef CONFIG_ZCRYPT_DEBUG
+	if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL)
+		ap_msg->flags ^= AP_MSG_FLAG_SPECIAL;
+#endif
+
 	return 0;
 }