]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
crypto: zynqmp-aes: Fix for segfault seen with large sets of data
authorKalyani Akula <kalyani.akula@xilinx.com>
Wed, 10 Oct 2018 09:20:39 +0000 (14:50 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Mon, 15 Oct 2018 06:17:55 +0000 (08:17 +0200)
This patch resolves the issue seen when encrypt/decrypt of large sets of
data.

Issue is seen due to improper handing of blkcipher_walk. When large
amount of data is sent for encrypt/decrypt, af_alg framework copies it
to the TSGL (TX SGL) kernel. When driver does a walk on the scatterlist,
walk returns nbytes to be processed. nbytes can be complete data sent
from user space or few bytes. So, when data is received in multiple
walks, we are accumulating the data and sending complete data at once.

Signed-off-by: Kalyani Akula <kalyani.akula@xilinx.com>
Reviewed-by: Appana Durga Kedareswara rao <appana.durga.rao@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/crypto/zynqmp-aes.c

index ce38c87986328069ade28d3224b2af982309960b..f9f938fb3b7053f1126d4b541a20d4faa1899d08 100644 (file)
@@ -27,6 +27,8 @@
 #define ZYNQMP_AES_SIZE_ERR                    0x06
 #define ZYNQMP_AES_WRONG_KEY_SRC_ERR           0x13
 
+#define ZYNQMP_AES_BLOCKSIZE                   0x04
+
 struct zynqmp_aes_dev {
        struct list_head list;
        struct device *dev;
@@ -108,34 +110,29 @@ static int zynqmp_setkeytype(struct crypto_tfm *tfm, const u8 *keytype,
        return 0;
 }
 
-static int zynqmp_aes_decrypt(struct blkcipher_desc *desc,
-                             struct scatterlist *dst,
-                             struct scatterlist *src,
-                             unsigned int nbytes)
+static int zynqmp_aes_xcrypt(struct blkcipher_desc *desc,
+                            struct scatterlist *dst,
+                            struct scatterlist *src,
+                            unsigned int nbytes,
+                            unsigned int flags)
 {
-       /* TODO: Move the common functionality to one API */
        struct zynqmp_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
        struct zynqmp_aes_dev *dd = zynqmp_aes_find_dev(op);
        const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
+       int err, ret, copy_bytes, src_data = 0, dst_data = 0;
        dma_addr_t dma_addr, dma_addr_buf;
        struct zynqmp_aes_data *abuf;
        struct blkcipher_walk walk;
-       int err, ret, keytype;
+       unsigned int data_size;
        size_t dma_size;
        char *kbuf;
 
        if (!eemi_ops || !eemi_ops->aes)
                return -ENOTSUPP;
 
-       blkcipher_walk_init(&walk, dst, src, nbytes);
-       err = blkcipher_walk_virt(desc, &walk);
-       op->iv = walk.iv;
-       op->src = walk.src.virt.addr;
-       op->dst = walk.dst.virt.addr;
-       keytype = op->keytype;
-
-       if (keytype == ZYNQMP_AES_KUP_KEY)
-               dma_size = nbytes + ZYNQMP_AES_KEY_SIZE + ZYNQMP_AES_IV_SIZE;
+       if (op->keytype == ZYNQMP_AES_KUP_KEY)
+               dma_size = nbytes + ZYNQMP_AES_KEY_SIZE
+                       + ZYNQMP_AES_IV_SIZE;
        else
                dma_size = nbytes + ZYNQMP_AES_IV_SIZE;
 
@@ -149,105 +146,28 @@ static int zynqmp_aes_decrypt(struct blkcipher_desc *desc,
                dma_free_coherent(dd->dev, dma_size, kbuf, dma_addr);
                return -ENOMEM;
        }
-       memcpy(kbuf, op->src, nbytes);
-       memcpy(kbuf + nbytes, op->iv, ZYNQMP_AES_IV_SIZE);
 
-       abuf->src = dma_addr;
-       abuf->dst = dma_addr;
-       abuf->iv = abuf->src + nbytes;
-       abuf->size = nbytes - ZYNQMP_AES_GCM_SIZE;
-       abuf->optype = ZYNQMP_AES_DECRYPT;
-       abuf->keysrc = keytype;
-       if (keytype == ZYNQMP_AES_KUP_KEY) {
-               memcpy(kbuf + nbytes + ZYNQMP_AES_IV_SIZE,
-                      op->key, ZYNQMP_AES_KEY_SIZE);
-               abuf->key = abuf->iv + ZYNQMP_AES_IV_SIZE;
-       } else {
-               abuf->key = 0;
-       }
-       eemi_ops->aes(dma_addr_buf, &ret);
-       if (ret != 0) {
-               switch (ret) {
-               case ZYNQMP_AES_GCM_TAG_MISMATCH_ERR:
-                       dev_err(dd->dev, "ERROR: Gcm Tag mismatch\n\r");
-                       break;
-               case ZYNQMP_AES_SIZE_ERR:
-                       dev_err(dd->dev, "ERROR : Non word aligned data\n\r");
-                       break;
-               case ZYNQMP_AES_WRONG_KEY_SRC_ERR:
-                       dev_err(dd->dev, "ERROR: Wrong KeySrc, enable secure mode\n\r");
-                       break;
-               default:
-                       dev_err(dd->dev, "ERROR: Invalid");
-                       break;
-               }
-               goto END;
-       }
-       memcpy(walk.dst.virt.addr, kbuf, nbytes - ZYNQMP_AES_GCM_SIZE);
-END:
-       err = blkcipher_walk_done(desc, &walk, 0);
-       dma_free_coherent(dd->dev, dma_size, kbuf, dma_addr);
-       dma_free_coherent(dd->dev, sizeof(struct zynqmp_aes_data),
-                         abuf, dma_addr_buf);
-       return err;
-}
-
-static int zynqmp_aes_encrypt(struct blkcipher_desc *desc,
-                             struct scatterlist *dst,
-                             struct scatterlist *src,
-                             unsigned int nbytes)
-{
-       struct zynqmp_aes_op *op = crypto_blkcipher_ctx(desc->tfm);
-       struct zynqmp_aes_dev *dd = zynqmp_aes_find_dev(op);
-       const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops();
-       dma_addr_t dma_addr, dma_addr_buf;
-       struct zynqmp_aes_data *abuf;
-       struct blkcipher_walk walk;
-       unsigned int data_size;
-       size_t dma_size;
-       u32 keytype;
-       char *kbuf;
-       int err, ret;
-
-       if (!eemi_ops || !eemi_ops->aes)
-               return -ENOTSUPP;
-
-       blkcipher_walk_init(&walk, dst, src, nbytes);
+       data_size = nbytes;
+       blkcipher_walk_init(&walk, dst, src, data_size);
        err = blkcipher_walk_virt(desc, &walk);
        op->iv = walk.iv;
-       op->src = walk.src.virt.addr;
-       op->dst = walk.dst.virt.addr;
-       keytype = op->keytype;
 
-       if (keytype == ZYNQMP_AES_KUP_KEY)
-               dma_size = nbytes + ZYNQMP_AES_KEY_SIZE
-                       + ZYNQMP_AES_IV_SIZE;
-       else
-               dma_size = nbytes + ZYNQMP_AES_IV_SIZE;
-
-       kbuf = dma_alloc_coherent(dd->dev, dma_size, &dma_addr, GFP_KERNEL);
-
-       if (!kbuf)
-               return -ENOMEM;
-
-       abuf = dma_alloc_coherent(dd->dev, sizeof(struct zynqmp_aes_data),
-                                 &dma_addr_buf, GFP_KERNEL);
-       if (!abuf) {
-               dma_free_coherent(dd->dev, dma_size, kbuf, dma_addr);
-               return -ENOMEM;
+       while ((nbytes = walk.nbytes)) {
+               op->src = walk.src.virt.addr;
+               memcpy(kbuf + src_data, op->src, nbytes);
+               src_data = src_data + nbytes;
+               nbytes &= (ZYNQMP_AES_BLOCKSIZE - 1);
+               err = blkcipher_walk_done(desc, &walk, nbytes);
        }
-       data_size = nbytes - ZYNQMP_AES_GCM_SIZE;
-       memcpy(kbuf, op->src, data_size);
        memcpy(kbuf + data_size, op->iv, ZYNQMP_AES_IV_SIZE);
-
        abuf->src = dma_addr;
        abuf->dst = dma_addr;
        abuf->iv = abuf->src + data_size;
-       abuf->size = data_size;
-       abuf->optype = ZYNQMP_AES_ENCRYPT;
-       abuf->keysrc = keytype;
+       abuf->size = data_size - ZYNQMP_AES_GCM_SIZE;
+       abuf->optype = flags;
+       abuf->keysrc = op->keytype;
 
-       if (keytype == ZYNQMP_AES_KUP_KEY) {
+       if (op->keytype == ZYNQMP_AES_KUP_KEY) {
                memcpy(kbuf + data_size + ZYNQMP_AES_IV_SIZE,
                       op->key, ZYNQMP_AES_KEY_SIZE);
 
@@ -256,8 +176,12 @@ static int zynqmp_aes_encrypt(struct blkcipher_desc *desc,
                abuf->key = 0;
        }
        eemi_ops->aes(dma_addr_buf, &ret);
+
        if (ret != 0) {
                switch (ret) {
+               case ZYNQMP_AES_GCM_TAG_MISMATCH_ERR:
+                       dev_err(dd->dev, "ERROR: Gcm Tag mismatch\n\r");
+                       break;
                case ZYNQMP_AES_SIZE_ERR:
                        dev_err(dd->dev, "ERROR : Non word aligned data\n\r");
                        break;
@@ -270,23 +194,50 @@ static int zynqmp_aes_encrypt(struct blkcipher_desc *desc,
                }
                goto END;
        }
-       memcpy(walk.dst.virt.addr, kbuf, nbytes);
+       if (flags)
+               copy_bytes = data_size;
+       else
+               copy_bytes = data_size - ZYNQMP_AES_GCM_SIZE;
+
+       blkcipher_walk_init(&walk, dst, src, copy_bytes);
+       err = blkcipher_walk_virt(desc, &walk);
+
+       while ((nbytes = walk.nbytes)) {
+               memcpy(walk.dst.virt.addr, kbuf + dst_data, nbytes);
+               dst_data = dst_data + nbytes;
+               nbytes &= (ZYNQMP_AES_BLOCKSIZE - 1);
+               err = blkcipher_walk_done(desc, &walk, nbytes);
+       }
 END:
-       err = blkcipher_walk_done(desc, &walk, 0);
        dma_free_coherent(dd->dev, dma_size, kbuf, dma_addr);
        dma_free_coherent(dd->dev, sizeof(struct zynqmp_aes_data),
                          abuf, dma_addr_buf);
-
        return err;
 }
 
+static int zynqmp_aes_decrypt(struct blkcipher_desc *desc,
+                             struct scatterlist *dst,
+                             struct scatterlist *src,
+                             unsigned int nbytes)
+{
+       return zynqmp_aes_xcrypt(desc, dst, src, nbytes, ZYNQMP_AES_DECRYPT);
+}
+
+static int zynqmp_aes_encrypt(struct blkcipher_desc *desc,
+                             struct scatterlist *dst,
+                             struct scatterlist *src,
+                             unsigned int nbytes)
+{
+       return zynqmp_aes_xcrypt(desc, dst, src, nbytes, ZYNQMP_AES_ENCRYPT);
+}
+
 static struct crypto_alg zynqmp_alg = {
        .cra_name               =       "xilinx-zynqmp-aes",
        .cra_driver_name        =       "zynqmp-aes",
        .cra_priority           =       400,
        .cra_flags              =       CRYPTO_ALG_TYPE_BLKCIPHER |
                                        CRYPTO_ALG_KERN_DRIVER_ONLY,
-       .cra_blocksize          =       4,
+       .cra_blocksize          =       ZYNQMP_AES_BLOCKSIZE,
        .cra_ctxsize            =       sizeof(struct zynqmp_aes_op),
        .cra_alignmask          =       15,
        .cra_type               =       &crypto_blkcipher_type,