s390/zcrypt: Introduce Failure Injection feature
authorHarald Freudenberger <freude@linux.ibm.com>
Tue, 29 Sep 2020 14:07:22 +0000 (16:07 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Wed, 7 Oct 2020 19:50:01 +0000 (21:50 +0200)
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>
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/ap_queue.c
drivers/s390/crypto/zcrypt_api.c
drivers/s390/crypto/zcrypt_api.h
drivers/s390/crypto/zcrypt_msgtype50.c
drivers/s390/crypto/zcrypt_msgtype6.c

index 03fd95b5a0e8e80b6fcaf0a04d74c69c3ef1fb5b..5029b80132aae638b2428d8f0fed2f5e42650926 100644 (file)
@@ -200,12 +200,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 */
@@ -213,7 +238,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.
index ada37f1c7ac71d00b6341177e5b9b9dfa121e578..13d4fe2c6127b928b2d9bc0376b2da768a22e383 100644 (file)
@@ -214,12 +214,20 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq)
 {
        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) {
index d642be65a3a08ee6e72c0d3416b07396b56cc7cd..08bb0f60c4b0f047cc5556bd18ad7befaf08d603 100644 (file)
@@ -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
@@ -645,6 +646,11 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms,
 
        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;
@@ -748,6 +754,11 @@ static long zcrypt_rsa_crt(struct ap_perms *perms,
 
        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;
@@ -852,6 +863,17 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
 
        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;
@@ -927,6 +949,14 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
        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);
@@ -995,6 +1025,11 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms,
 
        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) */
@@ -1377,10 +1412,24 @@ static int icarsamodexpo_ioctl(struct ap_perms *perms, unsigned long arg)
        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()))
@@ -1406,10 +1455,24 @@ static int icarsacrt_ioctl(struct ap_perms *perms, unsigned long arg)
        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()))
@@ -1435,10 +1498,24 @@ static int zsecsendcprb_ioctl(struct ap_perms *perms, unsigned long arg)
        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()))
@@ -1465,10 +1542,24 @@ static int zsendep11cprb_ioctl(struct ap_perms *perms, unsigned long arg)
        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()))
index 03804f03b5d05561267c3a1b79c310ae67ac066d..51c0b8bdef50ebb501b5ac6153b946582f74d89c 100644 (file)
@@ -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 */
index c543b59595099ef21bc10cfb81506d554ed2e601..bf14ee445f892ab8877df78f4788e50bda4f53b6 100644 (file)
@@ -246,6 +246,12 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_queue *zq,
            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(struct zcrypt_queue *zq,
            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;
 }
 
index aab7f06920353124581d6d9a4cfb84568ea03f2c..307f90657d1dd082a7ea3159ffe8dfc598dca00d 100644 (file)
@@ -482,6 +482,11 @@ static int XCRB_msg_to_type6CPRB_msgX(bool userspace, struct ap_message *ap_msg,
            || 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_msgx(bool userspace, struct ap_message *ap
        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;
 }